SPI既可以做主机也可以做从机。
当做主机时。MOSI,SCK,CS都是作为输出。 而作为从机时。MOSI,SCK,CS都是作为输入。
所以SPI的硬件电路应该实现这样的功能。
如下图做主机的数据流向:
如下图做从机的数据流向:

通过一些寄存器的配置来控制电路。跟GD32的差不多。
波特率配置越高,采样越快。SPI的速率越快。





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

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

NSS\SCK\MISO\MOSI 对应的 PA4\PA5\PA6\PA7引脚。
1. SPI时钟使能,SPI对应的GPIO时钟使能。复用时钟使能。
2. SPI的GOIP配置。
3. SPI的初始化配置
4. SPI使能。
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); /* 把接收到的数据返回(从接收缓冲器里拿出) */
- }