GD32F103 硬件SPI通信

这篇具有很好参考价值的文章主要介绍了GD32F103 硬件SPI通信。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1. SPI的通信原理

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

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

gd32 spi flash,GD32F10X,单片机,stm32 所以SPI的硬件电路应该实现这样的功能。

2. GD32/STM32的SPI框图 

1. GD32框图

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

gd32 spi flash,GD32F10X,单片机,stm32 

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

 gd32 spi flash,GD32F10X,单片机,stm32

2. STM32框图 

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

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

gd32 spi flash,GD32F10X,单片机,stm32

3. SPI的寄存器介绍

 1. 控制寄存器0(SPI_CTL0)

gd32 spi flash,GD32F10X,单片机,stm32 

gd32 spi flash,GD32F10X,单片机,stm32 

2. 控制寄存器1(SPI_CTL1) 

gd32 spi flash,GD32F10X,单片机,stm32

3. 状态寄存器(SPI_STAT)  

gd32 spi flash,GD32F10X,单片机,stm32

gd32 spi flash,GD32F10X,单片机,stm32 

4. 数据寄存器(SPI_DATA)  

gd32 spi flash,GD32F10X,单片机,stm32

4. SPI主模式配置

 gd32 spi flash,GD32F10X,单片机,stm32

1. 发送数据 

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

gd32 spi flash,GD32F10X,单片机,stm32

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文章来源地址https://www.toymoban.com/news/detail-782713.html

#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); /* 把接收到的数据返回(从接收缓冲器里拿出) */
}

到了这里,关于GD32F103 硬件SPI通信的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • STM32F103单片机通过SPI全双工通信协议与W25Q64(FLASH)进行通信【串行同步通信(数据线与时钟线配合),(一主多从模式)】附相关驱动代码详解

    1.W25Qxx系列是一种低成本、小型化、使用简单的 非易失性存储器 ,常应用于数据存储、字库存储、固件程序存储等场景 2.存储介质: Nor Flash(闪存) 3.时钟频率:80MHz / 160MHz (Dual SPI) / 320MHz (Quad SPI) 4.存储容量(24位地址): W25Q40: 4Mbit / 512KByte W25Q80: 8Mbit / 1MByte W25Q16: 16

    2024年04月13日
    浏览(23)
  • GD32F103VE侵入事件

    GD32F103VE的TAMPER引脚(PC13),当PC13输入低电平时,会产生一个侵入检测事件。它会将所有“ 数据备份寄存器 ”内容清除。 这个功能有什么用? 一是防止被人开壳,抄袭。二是自毁功能。 直奔主题,多一句就是浪费时间。测试程序如下:  main.c程序 如下:  

    2024年02月14日
    浏览(16)
  • GD32F103x 定时器

    STM32的定时器主要分为三种: 高级定时器、通用定时器、基本定时器 。 即:高级定时器具有捕获/比较通道和互补输出,死区时间,通用定时器只有捕获/比较通道,基本定时器没有以上两者。  1. 基本定时器 1.时钟源 时钟源来自RCC的CK_TIMER,就是内部时钟(CK_INT)直接经过控

    2024年02月07日
    浏览(17)
  • STM32F103硬件SPI控制6针/7针0.96寸OLED显示屏

    OLED主要参数 1、高分辨率:128 64(和12864LCD相同分辨率,但该OLED屏的单位面积像素点多)。 2、广可视角度:大于160°。 3、低功耗:正常显示时0.04W。 4、宽供电范围:直流3.3V-5V。 5、工业级:工作温度范围-30°℃~70°℃。 6、体积小:28.8mm 28.5mm。 7、通信方式:lIC、SPI。 8、亮

    2024年02月17日
    浏览(39)
  • GD32F103VET输出PWM波形

    GD32F103VET将TIMER0_CH3映射到PE14引脚,使其输出PWM波形。测试时,使用示波器看PE14引脚输出的波形,效果更直观。 TIMER0之PWM输出引脚映射如下: TIMER0_REMAP[1:0]=\\\"00\\\"(没有映射): TIMER0_CH0默认被映射到PA8引脚 TIMER0_CH1默认被映射到PA9引脚 TIMER0_CH2默认被映射到PA10引脚 TIMER0_CH3默认被映射

    2024年02月14日
    浏览(15)
  • 基于gd32f103移植freemodbus master 主栈

    1.移植freemodbus master需要先移植RT-Thread操作系统 GD32F103C8T6移植 RTT Nano 教程-CSDN博客 2.移植freemodbus master协议栈 在移植了RTT以后,我们需要移植就只有串口相关的函数 移植freemodbus master协议栈具体步骤 下载移植freemodbus master协议栈 源码 添加协议栈文件 向mdk添加头文件路径 修改

    2024年01月18日
    浏览(17)
  • GD32F103串口DMA收发(空闲中断 + DMA)

    GD32F103串口DMA收发(空闲中断 + DMA) 代码如下:

    2024年02月12日
    浏览(16)
  • 【STM32CubeIDE】STM32F103硬件SPI驱动1.8寸TFT LCD128X160 ST7735S屏幕

    ✨虽然STM32CubeIDE也是基于HAL库,但是还是不能直接转Keil MDK工程,本驱动案例从GitHub下载来的,原始工程使用的是较老版本的STM32CubeIDE配置的,没法在继续在新版本上程序配置,工程只是做了初始化配置。程序并不能直接被点亮,重新配置了lcd初始化函数以及复位函数后,才

    2024年02月12日
    浏览(15)
  • gd32f103vbt6 串口OTA升级5-combin部分

    本文主要是bin文件的组成进行一些简单介绍,方便理解升级的过程。 2.1 rk3399cpu+gd32f103 2.2 连接方式:串口(115200,8N1)或者iic(本文没有介绍iic) 3.1 单片机端分两个部分:iap(用于升级)和app(自己的应用)部分(这两个部分本文不做介绍)。 3.2 linux端做一个升级的app软件

    2024年02月16日
    浏览(14)
  • gd32f103vbt6 串口OTA升级3-linux端的部分

    本文主要是对linux端升级单片机程序的功能部分做一些介绍,包括一些软件流程。 2.1 rk3399cpu+gd32f103 2.2 连接方式:串口(115200,8N1)或者iic(本文没有介绍iic) 3.4.1  0 ~(0x5c00-1) : iap程序区,用于存放iap程序 3.4.2  0x5c00~(0x6000-1) : 这个1k用于存放一些标志位,以及程序的

    2024年02月17日
    浏览(19)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包