GD32F103 硬件SPI通信

news2024/12/24 8:48:20

1. SPI的通信原理

SPI既可以做主机也可以做从机。

当做主机时。MOSI,SCK,CS都是作为输出。 而作为从机时。MOSI,SCK,CS都是作为输入。

 所以SPI的硬件电路应该实现这样的功能。

2. GD32/STM32的SPI框图 

1. GD32框图

如下图做主机的数据流向:

 

如下图做从机的数据流向: 

 

2. STM32框图 

通过一些寄存器的配置来控制电路。跟GD32的差不多。

波特率配置越高,采样越快。SPI的速率越快。

3. SPI的寄存器介绍

 1. 控制寄存器0(SPI_CTL0)

 

 

2. 控制寄存器1(SPI_CTL1) 

3. 状态寄存器(SPI_STAT 

 

4. 数据寄存器(SPI_DATA 

4. SPI主模式配置

 

1. 发送数据 

先判断发送主机发送缓冲器是否为空。

2. 接收数据

接收数据缓冲器是否为空。如果为空就等待,否则就接收。

 

5. dome (硬件SPI访问w25Q32)

NSS\SCK\MISO\MOSI  对应的 PA4\PA5\PA6\PA7引脚。

1. 具体的SPI配置步骤。

1. SPI时钟使能,SPI对应的GPIO时钟使能。复用时钟使能。

2. SPI的GOIP配置。

3. SPI的初始化配置

4. SPI使能。

2. 代码实现

spi.h

#ifndef _SPI_H
#define _SPI_H

#include "gd32f10x.h"


void w25qxx_rcu_init(void);
void w25qxx_io_init(void);
void w25qxx_spi_init(void);

#endif

spi.c

#include "spi.h"

// 使能外设时钟
void w25qxx_rcu_init(void)
{
	rcu_periph_clock_enable(RCU_GPIOA); //使能GPIOA时钟
	rcu_periph_clock_enable(RCU_AF);    //使能AF时钟
	rcu_periph_clock_enable(RCU_SPI0);  //使能SPI0时钟
}
	
// IO口进行配置,使之复用为SPI0, PA4\PA5\PA6\PA7,NSS\SCK\MISO\MOSI
void w25qxx_io_init(void)
{
	gpio_init(GPIOA, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, GPIO_PIN_6); // MISO 浮空输入
	
	gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_5 | GPIO_PIN_7); // SCK\MOSI 复用推挽
	
	gpio_init(GPIOA, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_4);// NSS片选口 普通的推挽输出
}
	
// SPI0初始化
void w25qxx_spi_init(void)
{
	spi_parameter_struct spi_struct;
	spi_struct.device_mode = SPI_MASTER;                    /*!< SPI master  做主机*/
  spi_struct.trans_mode = SPI_TRANSMODE_FULLDUPLEX;         /*!< SPI transfer type 全双工 */
  spi_struct.frame_size =  SPI_FRAMESIZE_8BIT;              /*!< SPI frame size  一次8字节 */
  spi_struct.nss = SPI_NSS_SOFT;                            /*!< SPI NSS control by software 软件CS */
  spi_struct.endian = SPI_ENDIAN_MSB;                       /*!< SPI big endian or little endian  传输高字节在前*/
  spi_struct.clock_polarity_phase = SPI_CK_PL_LOW_PH_1EDGE; /*!< SPI clock phase and polarity 空闲低电平 第一个边沿进行采样*/
  spi_struct.prescale = SPI_PSC_8;                          /*!< SPI prescaler factor 8分频*/
	spi_init(SPI0, &spi_struct);
}

w25qxx.h 

#ifndef _W25QXX_SPI_H
#define _W25QXX_SPI_H

#include "gd32f10x.h"
#include "w25qxx_ins.h"
#include "gd32f10x_spi.h"

#define W25QXX_ID_1           1

#define W25QXX_SR_ID_1        1
#define W25QXX_SR_ID_2        2
#define W25QXX_SR_ID_3        3
void w25qxx_init(void);
void w25qxx_wait_busy(void);
uint8_t w25qxx_read_sr(uint8_t sregister_id);  // 读状态寄存器
	
void w25qxx_read(uint8_t *p_buffer, uint32_t read_addr, uint16_t num_read_bytes);

void w25qxx_write(uint8_t *p_buffer, uint32_t write_addr, uint16_t num_write_bytes);
void w25qxx_write_nocheck(uint8_t *p_buffer, uint32_t write_addr, uint16_t num_write_bytes); //
void w25qxx_write_page(uint8_t *p_buffer, uint32_t write_addr, uint16_t num_write_bytes);   // page program

void w25qxx_erase_sector(uint32_t sector_addr);
void w25qxx_erase_chip(void);

void w25qxx_write_enable(void);
void w25qxx_write_disable(void);

void w25qxx_power_down(void);
void w25qxx_wake_up(void);

void w25qxx_cs_enable(uint8_t cs_id);
void w25qxx_cs_disable(uint8_t cs_id);
uint8_t w25qxx_swap(uint8_t byte_to_send);


#endif

w25qxx.c

#include "w25qxx.h"
#include "spi.h"

void w25qxx_init(void){
	// 使能外设时钟
	w25qxx_rcu_init();
	
	// IO口进行配置,使之复用为SPI0, PA4\PA5\PA6\PA7,NSS\SCK\MISO\MOSI
	w25qxx_io_init();
	
	// SPI0初始化
	w25qxx_spi_init();
	// SPI使能
	spi_enable(SPI0);
}


// 如果SR-1的BUSY位为1的话,一直等待,直到BUSY位为0,结束等待
void w25qxx_wait_busy(void){
	while((w25qxx_read_sr(W25QXX_SR_ID_1) & 0x01) == 0x01){
		;
	}
}

// 读状态寄存器
uint8_t w25qxx_read_sr(uint8_t sregister_id){
	uint8_t command, result;
	switch(sregister_id){
		case W25QXX_SR_ID_1:
			command = W25QXX_READ_STATUS_REGISTER_1;
		break;
		case W25QXX_SR_ID_2:
			command = W25QXX_READ_STATUS_REGISTER_2;
		break;
		case W25QXX_SR_ID_3:
			command = W25QXX_READ_STATUS_REGISTER_3;
		break;
		default:
			command = W25QXX_READ_STATUS_REGISTER_1;
		break;
	}
	
	w25qxx_cs_enable(W25QXX_ID_1);
	w25qxx_swap(command);
	result = w25qxx_swap(0xFF);
	w25qxx_cs_disable(W25QXX_ID_1);
	
	return result;
}

// 读flash的数据
// *p_buffer 读回的数据的存放位置
void w25qxx_read(uint8_t *p_buffer, uint32_t read_addr, uint16_t num_read_bytes){
	uint16_t i;
	
	w25qxx_cs_enable(W25QXX_ID_1);
	
	w25qxx_swap(W25QXX_READ_DATA); //发送读数据的指令
	w25qxx_swap(read_addr >> 16);  //发送24bit地址
	w25qxx_swap(read_addr >> 8);
	w25qxx_swap(read_addr);
	
	for(i=0; i < num_read_bytes; i++){
		p_buffer[i] = w25qxx_swap(0xFF);
	}
	
	w25qxx_cs_disable(W25QXX_ID_1);
}

// 
uint8_t W25QXX_Buffer[4096];  //用来存放从sector读出的bytes
void w25qxx_write(uint8_t *p_buffer, uint32_t write_addr, uint16_t num_write_bytes){
	uint32_t sec_num;
	uint16_t sec_remain;
	uint16_t sec_off;
	uint16_t i;
	
	sec_num	= write_addr / 4096;              //要写入的位置处在第sec_num个扇区上
	sec_off = write_addr % 4096;
	
	sec_remain = 4096 - sec_off;
	
	if(num_write_bytes <= sec_remain){
		w25qxx_read(W25QXX_Buffer, sec_num * 4096, 4096);  //扇区的数据读出来
		
		for(i = 0; i < sec_remain; i++){
			if(W25QXX_Buffer[i + sec_off] != 0xFF)  //说明这个扇区的第i+sec_off位没有擦除
				break;
		}
		
		if(i < sec_remain){ // 扇区没有擦除
			w25qxx_erase_sector(sec_num * 4096);
			for(i = 0; i < sec_remain; i++){
				W25QXX_Buffer[i + sec_off] = p_buffer[i];
			}
			w25qxx_write_nocheck(W25QXX_Buffer, sec_num * 4096, 4096);
		}else{              // 扇区sec_remain部分是擦除过的
			w25qxx_write_nocheck(p_buffer, write_addr, num_write_bytes);
		}
	}else{
		w25qxx_read(W25QXX_Buffer, sec_num * 4096, 4096);  //扇区的数据读出来
		
		for(i = 0; i < sec_remain; i++){
			if(W25QXX_Buffer[i + sec_off] != 0xFF)  //说明这个扇区的第i+sec_off位没有擦除
				break;
		}
		
		if(i < sec_remain){ // 扇区没有擦除
			w25qxx_erase_sector(sec_num * 4096);
			for(i = 0; i < sec_remain; i++){
				W25QXX_Buffer[i + sec_off] = p_buffer[i];
			}
			w25qxx_write_nocheck(W25QXX_Buffer, sec_num * 4096, 4096);
		}else{              // 扇区sec_remain部分是擦除过的
			w25qxx_write_nocheck(p_buffer, write_addr, sec_remain);
		}
		
		write_addr += sec_remain;
		p_buffer += sec_remain;
		num_write_bytes -= sec_remain;
		w25qxx_write(p_buffer, write_addr, num_write_bytes);
	}
		
	//判断读出来的数据是否都为0xFF
	;//扇区是否删除
	 //判断是否跨页
}

// 调用之前先确保扇区删除
void w25qxx_write_nocheck(uint8_t *p_buffer, uint32_t write_addr, uint16_t num_write_bytes){
	uint16_t page_remain = 256 - write_addr % 256;
	
	if(num_write_bytes <= page_remain){
		w25qxx_write_page(p_buffer, write_addr, num_write_bytes);
	}else{
		w25qxx_write_page(p_buffer, write_addr, page_remain);
		p_buffer += page_remain;
		write_addr += page_remain;
		num_write_bytes -= page_remain;
		w25qxx_write_nocheck(p_buffer, write_addr, num_write_bytes);
	}
}

// page program
// 保证没有跨页写的前提下调用此函数往某个页上写内容
void w25qxx_write_page(uint8_t *p_buffer, uint32_t write_addr, uint16_t num_write_bytes){
	uint16_t i;
	
	w25qxx_write_enable();
	
	w25qxx_cs_enable(W25QXX_ID_1);
	w25qxx_swap(W25QXX_PAGE_PROGRAM);
	w25qxx_swap(write_addr >> 16);  //发送24bit地址
	w25qxx_swap(write_addr >> 8);
	w25qxx_swap(write_addr);
	
	for(i = 0; i < num_write_bytes; i++){
		w25qxx_swap(p_buffer[i]);
	}
	w25qxx_cs_disable(W25QXX_ID_1);
	
	w25qxx_wait_busy();
}

void w25qxx_erase_sector(uint32_t sector_addr){
	w25qxx_write_enable();
	
	w25qxx_cs_enable(W25QXX_ID_1);
	w25qxx_swap(W25QXX_SECTOR_ERASE_4KB);
	w25qxx_swap(sector_addr >> 16);
	w25qxx_swap(sector_addr >> 8);
	w25qxx_swap(sector_addr);
	w25qxx_cs_disable(W25QXX_ID_1);
	
	w25qxx_wait_busy();
}

void w25qxx_erase_chip(void){
	w25qxx_write_enable();
	
	w25qxx_cs_enable(W25QXX_ID_1);
	w25qxx_swap(W25QXX_CHIP_ERASE);
	w25qxx_cs_disable(W25QXX_ID_1);
	
	w25qxx_wait_busy();
}

void w25qxx_write_enable(void){
	w25qxx_cs_enable(W25QXX_ID_1);
	w25qxx_swap(W25QXX_WRITE_ENABLE);
	w25qxx_cs_disable(W25QXX_ID_1);
}

void w25qxx_write_disable(void){
	w25qxx_cs_enable(W25QXX_ID_1);
	w25qxx_swap(W25QXX_WRITE_DISABLE);
	w25qxx_cs_disable(W25QXX_ID_1);
}

// 低电量休眠
void w25qxx_power_down(void){
	w25qxx_cs_enable(W25QXX_ID_1);
	w25qxx_swap(W25QXX_POWER_DOWN);
	w25qxx_cs_disable(W25QXX_ID_1);
}

// 唤醒
void w25qxx_wake_up(void){
	w25qxx_cs_enable(W25QXX_ID_1);
	w25qxx_swap(W25QXX_RELEASE_POWER_DOWN_HPM_DEVICE_ID);
	w25qxx_cs_disable(W25QXX_ID_1);
}

/*
brief:使能片选引脚cs
cs_id: cs引脚的序号,即第几个w25qxx flash
*/
void w25qxx_cs_enable(uint8_t cs_id){
	switch(cs_id){
		case W25QXX_ID_1:
			gpio_bit_reset(GPIOA, GPIO_PIN_4);
		break;
		default:
			break;
	}
}

void w25qxx_cs_disable(uint8_t cs_id){
	switch(cs_id){
		case W25QXX_ID_1:
			gpio_bit_set(GPIOA, GPIO_PIN_4);
		break;
		default:
			break;
	}
}

/*
主从数据交换
*/
uint8_t w25qxx_swap(uint8_t byte_to_send){
	while(spi_i2s_flag_get(SPI0, SPI_FLAG_TBE) == RESET){ // 等待SPI发送缓冲器为空
		;
	}
	spi_i2s_data_transmit(SPI0, byte_to_send);            // 把数据放到发生缓冲器
	while(spi_i2s_flag_get(SPI0, SPI_FLAG_TRANS) == SET){ // 等待通信结束
		;
	}
	
	while(spi_i2s_flag_get(SPI0, SPI_FLAG_RBNE) == RESET){ // 等待SPI接收缓冲器非空
		;
	}	
	return spi_i2s_data_receive(SPI0); /* 把接收到的数据返回(从接收缓冲器里拿出) */
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

华为HCIP安全 VPN学习笔记 密码学基础:基本框架介绍

0.学习密码学的目的与方法 作为一个工程师&#xff0c;你不需要像科学家一样掌握密码学的整个开发过程。 为了保障安全性&#xff0c;密码学发展出不同的模块&#xff0c;解决了不同的问题&#xff0c;数据的安全性也在这个过程中不断提升。而加密只是其中众多模块之一。其他…

JAVA毕业设计098—基于Java+Springboot的在线教育课程视频(源码+数据库)

基于JavaSpringboot的在线教育课程视频(源码数据库)098 一、系统介绍 本系统分为管理员、教师、用户三种角色(角色菜单可自行分配) 用户功能&#xff1a; 注册、登录、课程搜索、视频观看、课程资料发布、资料浏览、用户中心、我的发布、通知信息、密码修改 教师功能&…

母婴店怎么在微信小程序卖东西

随着互联网的发展&#xff0c;微信小程序已经成为一种新型的电商模式&#xff0c;它无需下载安装&#xff0c;使用方便&#xff0c;不占用手机内存&#xff0c;让购物变得更加简单便捷。母婴店也可以通过微信小程序来销售产品&#xff0c;拓宽销售渠道&#xff0c;增加销售额。…

Docker Mysql实战:docker compose 搭建Mysql

1、docker-compose-mysql文件准备 进入/home/docker目录&#xff0c;新建docker-compose-mysql.yml文件&#xff0c;内容如下&#xff1a; version: 3.0 services:mysql:image: "mysql:5.7"container_name: "mysql"environment:MYSQL_ROOT_PASSWORD: &q…

还原回收站的文件需要管理员权限怎么办

案例分享&#xff1a;无意中删除一些文件至回收站&#xff0c;却无法还原&#xff0c;提示“您需要权限来执行此操作”。怎么办&#xff0c;请大侠指点&#xff1f; ——当我们意外地删除了文件并将其放入回收站时&#xff0c;我们通常可以轻松地将其还原。然而&#xff0c;有…

Springboot+vue的企业OA管理系统(有报告),Javaee项目,springboot vue前后端分离项目。

演示视频&#xff1a; Springbootvue的企业OA管理系统&#xff08;有报告&#xff09;&#xff0c;Javaee项目&#xff0c;springboot vue前后端分离项目。 项目介绍&#xff1a; 本文设计了一个基于Springbootvue的前后端分离的企业OA管理系统&#xff0c;采用M&#xff08;m…

如何正确操作封箱机

前文跟大家分享过封箱机错误操作三案例&#xff0c;那么封箱机到底如何才能正确操作呢&#xff1f;今天就和您分享一下如何正确操作封箱机。 1、确定正确的电源电压进行接入。目前国内封箱机均采用220v 50hz电源电压&#xff0c;但也有一些定制型设备可能使用380v电源&#xff…

vue建项目

vue3 create-vue 建vue3项目 vscode里改点东西&#xff0c;首先vetur禁用&#xff0c;这个是vue2的&#xff0c;下volar pinia持久化插件&#xff1a;npm i pinia-plugin-persistedstate 配eslint、prettier 在.eslintrc.cjs里配 rules: {// prettier专注于代码的美观度 (格…

Go 团队发布组织 / 构建 Go module 的官方指南

导读Go 团队发布了一份官方指南&#xff0c;帮助开发者更规范地组织 / 构建 Go module。 刚接触 Go 的开发者常见问题之一是&#xff0c;“就文件和文件夹的组织布局而言&#xff0c;如何组织我的 Go 项目&#xff1f;”。这份指南就是提供建议来帮助回答这个问题。其中包括针对…

2023年中国CEM-1型覆铜板产量、需求量及行业销售收入分析[图]

CEM-1指覆铜板的一种&#xff0c;以玻纤布半固化片与纸基半固化片层压铜箔达到固化后形成的板材&#xff0c;属于复合型基材&#xff0c;CEM-1能用来制作频率特性要求高的PCB&#xff0c;如电视机的调谐器、电源开关、超声波设备、计算机电源和键盘&#xff0c;也可以用于电视机…

解决扬声器异常

之前使用的是PulseAudio PulseAudio 是默认的音频服务器和音频框架&#xff0c;因此大多数应用程序通过 PulseAudio 来处理音频 但也有一些应用程序直接使用 ALSA&#xff08;Advanced Linux Sound Architecture&#xff09;来与音频硬件交互。在这些情况下&#xff0c;ALSA …

[GYCTF2020]Ezsqli 绕过or information_schema 无列名注入

https://www.cnblogs.com/h0cksr/p/16189749.html https://www.gem-love.com/ctf/1782.html 说好的ez.... 我们开始吧 首先就直接进行抓包 看回显 然后开始正常的测试 报错了 这里的 or过滤的绕过 我们可以使用 ^ 或者 || 我喜欢用 || 所以继续构造 发现了 就是目前这种…

JavaScript——数据类型、类型转换

数据类型 计算机世界中的万事万物都是数据。 计算机程序可以处理大量的数据&#xff0c;为什么要给数据分类? 更加充分和高效的利用内存也更加方便程序员的使用数据 基本数据类型 number 数字型 JavaScript中正数、负数、小数等统一称为number JS是弱数据类型&#xff0…

基于SpringBoot的篮球论坛系统

基于SpringBoot的篮球论坛系统~ 开发语言&#xff1a;Java数据库&#xff1a;MySQL技术&#xff1a;SpringBootMyBatisVue工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 系统展示 前台界面 登录界面 管理员界面 摘要 基于Spring Boot的篮球论坛系统代表了一个现代社交网络应…

iPhone序列号查询的4个常用方法,赶快get起来!

苹果手机序列号是每台设备的唯一标识符。就像每个人都拥有自己的身份证一样&#xff0c;序列号则是每台苹果手机的身份证明。 通过查询iPhone序列号&#xff0c;我们可以得知手机的产地、生产日期&#xff0c;以及机型等信息&#xff0c;还可以查验手机是否被激活&#xff0c;…

基于Stable Diffusion的图像合成数据集

当前从文本输入生成合成图像的模型不仅能够生成非常逼真的照片&#xff0c;而且还能够处理大量不同的对象。 在论文“评估使用稳定扩散生成的合成图像数据集”中&#xff0c;我们使用“稳定扩散”模型来研究哪些对象和类型表现得如此逼真&#xff0c;以便后续图像分类正确地分配…

华为OD七日集训第7期 - 按算法分类,由易到难,循序渐进,玩转OD

目录 一、适合人群二、本期训练时间三、如何参加四、7日集训第7期五、精心挑选21道高频100分经典题目&#xff0c;作为入门。第1天、逻辑分析第2天、字符串处理第3天、数据结构第4天、递归回溯第5天、二分查找第6天、深度优先搜索dfs算法第7天、动态规划 六、集训总结 大家好&a…

Qt QPen

文章目录 Qt::PenJoinStyleQt::PenCapStyleQt::PenStyleQPen Qt::PenJoinStyle 内容值说明Qt::MiterJoin0x00这些线的外边缘将延伸&#xff0c;以一定角度相交&#xff0c;并填充此区域。Qt::BevelJoin0x40两条线之间的三角形缺口被填满。Qt::RoundJoin0x80两条直线之间的圆弧…

Excel 快速分析

文章目录 格式化 快捷键: Ctrl Q 先选中数据, 再按快捷键或快速分析按钮. 格式化 查看规则: 前提是先在表中添加某种规则, 再全选该表, 这样在查看规则时才会显示出这个规则.

MT03A 毫欧与电机测试仪(EP130)

【MT03A 毫欧与电机测试仪&#xff08;EP130&#xff09;】 虹科的 MT03A 毫欧与电机测试仪是一款革命性的设备&#xff0c;改变了低电阻测试的方法。MT03A 可在一分钟内对三相电机上的所有绕组进行电阻测试&#xff0c;生成高精度的结果&#xff1b;附随的传感器探头可自动补偿…