2025年GD32F103 硬件SPI通信

GD32F103 硬件SPI通信1 SPI 的通信原理 SPI 既可以做主机也可以做从机 当做主机时 MOSI SCK CS 都是作为输出 而作为从机时 MOSI SCK CS 都是作为输入 所以 SPI 的硬件电路应该实现这样的功能 2 GD32 STM32 的 SPI 框图 1 GD32 框图 如下图做主机的数据流向

大家好,我是讯享网,很高兴认识大家。

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

小讯
上一篇 2025-01-12 13:15
下一篇 2025-03-17 15:03

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/52978.html