【STM32】SPI协议及W25Q128的应用(详细注释版)
实验目的读写W25Q128硬件接线代码main.cHAL_Init();/* 初始化HAL库 *//* 设置时钟, 72Mhz *///LED初始化led_init();//串口1初始化//W25Q128初始化printf("打印测试:hello world\r\n");led2_off();led1_off();led2_on();w25q128.c//SPI初始化//使用SPI1//作为主设备
1. IIC与SPI对比
- IIC 是半双工通讯,无法同时收发信息;SPI 是全双工通讯,可以同时收发信息;
- IIC 通讯协议较复杂,而 SPI 通讯协议较简单;
- IIC 需要通过地址选择从机,而 SPI 只需一个引脚即可选中从机;
- IIC 通讯速率一般为 100kHz 左右,而 SPI 可以达到 50MHz ;
- IIC 需要的通讯线较少,而 SPI 需要较多。
2. SPI是什么?
SPI是串行外设接口(Serial Peripheral Interface)的缩写,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的布局上节省空间,提供方便,正是出于这种简单易用的特性,越来越多的芯片集成了这种通信协议,比如 AT91RM9200 。
3. SPI物理架构

SPI 总线包含 4 条通讯线,分别为 CS、SCK、MOSI、MISO。它们的作用介绍如下 :
(1) MISO – Master Input Slave Output,主设备数据输入,从设备数据输出
(2) MOSI – Master Output Slave Input,主设备数据输出,从设备数据输入
(3) SCK – Serial Clock,时钟信号,由主设备产生
(4) CS – Chip Select,片选信号,由主设备控制
STM32F1 系列芯片有 3 个SPI 接口。
4. SPI工作原理



5. SPI工作模式
时钟极性(CPOL):
没有数据传输时时钟线的空闲状态电平
0:SCK在空闲状态保持低电平
1:SCK在空闲状态保持高电平
时钟相位(CPHA):
时钟线在第几个时钟边沿采样数据
0:SCK的第一(奇数)边沿进行数据位采样,数据在第一个时钟边沿被锁存
1:SCK的第二(偶数)边沿进行数据位采样,数据在第二个时钟边沿被锁存

模式 0 和模式 3 最常用。
模式 0 时序图:

模式 3 时序图:

6. SPI寄存器及库函数介绍










7. 小实验:读写W25Q128实验

实验目的
读写W25Q128
硬件接线


代码
main.c
#include "sys.h"
#include "uart1.h"
#include "delay.h"
#include "led.h"
#include "uart1.h"
#include "w25q128.h"
uint8_t data_write[4] = {0xAA, 0xBB, 0xCC, 0xDD};
uint8_t data_read[4] = {0};
int main(void)
{
HAL_Init(); /* 初始化HAL库 */
stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
//LED初始化
led_init();
//串口1初始化
uart1_init(115200);
//W25Q128初始化
w25q128_init();
printf("打印测试:hello world\r\n");
uint16_t device_id = w25q128_read_id();
printf("device id: %X\r\n", device_id);
//w25q128_erase_sector(0x000000);
//w25q128_write_page(0x000000, data_write, 4);
w25q128_read_data(0x000000, data_read, 4);
printf("data read: %X, %X, %X, %X\r\n",data_read[0],data_read[1],data_read[2],data_read[3]);
while(1)
{
led1_on();
led2_off();
delay_ms(500);
led1_off();
led2_on();
delay_ms(500);
}
}
w25q128.c
#include "w25q128.h"
SPI_HandleTypeDef spi_handle = {0};
//SPI初始化
void w25q128_spi_init(void)
{
spi_handle.Instance = SPI1; //使用SPI1
spi_handle.Init.Mode = SPI_MODE_MASTER; //作为主设备
spi_handle.Init.Direction = SPI_DIRECTION_2LINES; //全双工传输
spi_handle.Init.DataSize = SPI_DATASIZE_8BIT; //数据位长度位8bit
spi_handle.Init.CLKPolarity = SPI_POLARITY_LOW; //时钟极性为低电平
spi_handle.Init.CLKPhase = SPI_PHASE_1EDGE; //奇数边沿采样
spi_handle.Init.NSS = SPI_NSS_SOFT; //软件控制
spi_handle.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_256; //波特率256分频
spi_handle.Init.FirstBit = SPI_FIRSTBIT_MSB; //高位先行
spi_handle.Init.TIMode = SPI_TIMODE_DISABLE;
spi_handle.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
spi_handle.Init.CRCPolynomial = 7;
HAL_SPI_Init(&spi_handle);
}
//SPI硬件相关初始化
void HAL_SPI_MspInit(SPI_HandleTypeDef *hspi)
{
if(hspi->Instance == SPI1){
GPIO_InitTypeDef gpio_initstruct;
//使能GPIOA时钟
__HAL_RCC_GPIOA_CLK_ENABLE();
//使能SPI1时钟
__HAL_RCC_SPI1_CLK_ENABLE();
//调用GPIO初始化函数
gpio_initstruct.Pin = GPIO_PIN_4; //NSS对应引脚
gpio_initstruct.Mode = GPIO_MODE_OUTPUT_PP; //推挽输出
gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH; //高速
gpio_initstruct.Pull = GPIO_PULLUP; //上拉
HAL_GPIO_Init(GPIOA, &gpio_initstruct);
gpio_initstruct.Pin = GPIO_PIN_5 | GPIO_PIN_7; //CLK、MOSI对应引脚
gpio_initstruct.Mode = GPIO_MODE_AF_PP; //复用推挽输出
HAL_GPIO_Init(GPIOA, &gpio_initstruct);
gpio_initstruct.Pin = GPIO_PIN_6; //MISO对应引脚
gpio_initstruct.Mode = GPIO_MODE_INPUT; //输入模式
HAL_GPIO_Init(GPIOA, &gpio_initstruct);
}
}
//SPI字节数据交换(数据收发)
uint8_t w25q128_spi_swap_byte(uint8_t data)
{
uint8_t recv_data = 0;
HAL_SPI_TransmitReceive(&spi_handle, &data, &recv_data, 1, 1000); //数据收发
return recv_data;
}
//W25Q128初始化
void w25q128_init(void)
{
w25q128_spi_init();
}
//读取设备ID
uint16_t w25q128_read_id(void)
{
uint16_t device_id = 0;
//拉低片选
W25Q128_CS(0);
//写指令
w25q128_spi_swap_byte(FLASH_ManufactDeviceID);
w25q128_spi_swap_byte(0x00);
w25q128_spi_swap_byte(0x00);
w25q128_spi_swap_byte(0x00);
//获取device_id
device_id = w25q128_spi_swap_byte(FLASH_DummyByte) << 8;
device_id |= w25q128_spi_swap_byte(FLASH_DummyByte);
//拉高片选
W25Q128_CS(1);
return device_id;
}
//W25Q128写使能
void w25q128_write_enable(void)
{
//拉低片选
W25Q128_CS(0);
//写入指令
w25q128_spi_swap_byte(FLASH_WriteEnable);
//拉高片选
W25Q128_CS(1);
}
//W25Q128读取SR1寄存器
uint8_t w25q128_read_sr1(void)
{
uint8_t recv_data = 0;
//拉低片选
W25Q128_CS(0);
//发送指令
w25q128_spi_swap_byte(FLASH_ReadStatusReg1);
//读取SR1
recv_data = w25q128_spi_swap_byte(FLASH_DummyByte);
//拉高片选
W25Q128_CS(1);
return recv_data;
}
//发送地址
void w25q128_send_address(uint32_t address)
{
//从高位到低位依次发送
w25q128_spi_swap_byte(address >> 16);
w25q128_spi_swap_byte(address >> 8);
w25q128_spi_swap_byte(address);
}
//W25Q128读数据
void w25q128_read_data(uint32_t address, uint8_t *data, uint32_t size)
{
uint32_t i = 0;
//拉低片选
W25Q128_CS(0);
//发送指令
w25q128_spi_swap_byte(FLASH_ReadData);
//发送地址
w25q128_send_address(address);
//读取SR1
for(i = 0;i < size;i++){
data[i] = w25q128_spi_swap_byte(FLASH_DummyByte);
}
//拉高片选
W25Q128_CS(1);
}
//忙等待
void w25q128_wait_busy(void)
{
while((w25q128_read_sr1() & 0x01) == 0x01);
}
//W25Q128页写
void w25q128_write_page(uint32_t address, uint8_t *data, uint16_t size)
{
uint16_t i = 0;
//写使能
w25q128_write_enable();
//拉低片选
W25Q128_CS(0);
//发送指令
w25q128_spi_swap_byte(FLASH_PageProgram);
//发送地址
w25q128_send_address(address);
//写入数据
for(i = 0;i < size;i++){
w25q128_spi_swap_byte(data[i]);
}
//拉高片选
W25Q128_CS(1);
//忙等待
w25q128_wait_busy();
}
//W25Q128擦除扇区
void w25q128_erase_sector(uint32_t address)
{
//写使能
w25q128_write_enable();
//忙等待
w25q128_wait_busy();
//拉低片选
W25Q128_CS(0);
//发送指令
w25q128_spi_swap_byte(FLASH_SectorErase);
//发送地址
w25q128_send_address(address);
//拉高片选
W25Q128_CS(1);
//忙等待
w25q128_wait_busy();
}
w25q128.h
#ifndef __W25Q128_H__
#define __W25Q128_H__
#include "sys.h"
#define W25Q128_CS(x) do{ x ? \
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET) : \
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET); \
}while(0)
//指令表
#define FLASH_ManufactDeviceID 0x90
#define FLASH_WriteEnable 0x06
#define FLASH_ReadStatusReg1 0x05
#define FLASH_ReadData 0x03
#define FLASH_PageProgram 0x02
#define FLASH_SectorErase 0x20
#define FLASH_DummyByte 0xFF
//W25Q128初始化
void w25q128_init(void);
//读取设备ID
uint16_t w25q128_read_id(void);
//W25Q128读数据
void w25q128_read_data(uint32_t address, uint8_t *data, uint32_t size);
//忙等待
void w25q128_wait_busy(void);
//W25Q128页写
void w25q128_write_page(uint32_t address, uint8_t *data, uint16_t size);
//W25Q128擦除扇区
void w25q128_erase_sector(uint32_t address);
#endif
更多推荐



所有评论(0)