点击查看代码
/**
*******************************************************************************
* @file main.c
* @author
* @version
* @date 2024/07/12
* @brief 利用MCU的硬件SPI1实现对存储IC进行读取操作,掌握SPI工作模式和时序分析
SCK -- PB3 (SPI1_SCK)
MOSI -- PB5 (SPI1_MOSI)
MISO -- PB4 (SPI1_MISO)
CS -- PB14
*******************************************************************************
**/
#include "stm32f4xx.h" //必须包含
#include <stdio.h>
#include <stdbool.h>
//片选引脚
#define W25Q128_CS(n) (n) ? GPIO_SetBits(GPIOB,GPIO_Pin_14) : GPIO_ResetBits(GPIOB,GPIO_Pin_14)
//前台程序就是中断服务程序,该程序是不需要手动调用的,当中断触发之后CPU会自动跳转过来执行该函数
void USART1_IRQHandler(void)
{
uint8_t data;
//判断中断是否发生
if (USART_GetITStatus(USART1, USART_IT_RXNE) == SET)
{
//从USART1中接收一个字节
data = USART_ReceiveData(USART1); //一次只能接收一个字节
//把接收到的数据转发出去
USART_SendData(USART1,data);
}
}
//延时微秒 注意:Systick是24bit的递减计数器 约等于798915us,所以参数不可以超过这个值
void delay_us(u32 nus)
{
SysTick->CTRL = 0; // 向控制状态寄存器中写入0,目的关闭系统嘀嗒定时器
SysTick->LOAD = (nus * 21) - 1;// 指的是计数次数,定时时间 = 计数次数 * 计数周期
SysTick->VAL = 0; // 清除当前数值寄存器的值
SysTick->CTRL = 1; // 开启了定时器,并且定时器的时钟源选择了21MHZ--> 计数周期 = 1/21us
while ((SysTick->CTRL & 0x00010000)==0);//等待延时时间到达
SysTick->CTRL = 0; // 向控制状态寄存器中写入0,目的关闭系统嘀嗒定时器
}
//延时毫秒 注意:Systick是24bit的递减计数器 约等于798ms,所以参数不可以超过这个值
void delay_ms(u32 nms)
{
SysTick->CTRL = 0; // 向控制状态寄存器中写入0,目的关闭系统嘀嗒定时器
SysTick->LOAD = (nms * 21*1000) - 1;// 指的是计数次数,定时时间 = 计数次数 * 计数周期
SysTick->VAL = 0; // 清除当前数值寄存器的值
SysTick->CTRL = 1; // 开启了定时器,并且定时器的时钟源选择了21MHZ--> 计数周期 = 1/21us
while ((SysTick->CTRL & 0x00010000)==0);//等待延时时间到达
SysTick->CTRL = 0; // 向控制状态寄存器中写入0,目的关闭系统嘀嗒定时器
}
void USART1_Config(u32 baud)
{
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
//打开了GPIO端口时钟 PA9和PA10
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
//打开USART1的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
//选择GPIO引脚的复用功能
GPIO_PinAFConfig(GPIOA, GPIO_PinSource9 , GPIO_AF_USART1);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_USART1);
//配置GPIO引脚 注意:复用模式
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9|GPIO_Pin_10;
GPIO_Init(GPIOA, &GPIO_InitStructure);
//配置串口参数+对串口初始化
USART_InitStructure.USART_BaudRate = baud; //波特率
USART_InitStructure.USART_WordLength = USART_WordLength_8b; //数据位
USART_InitStructure.USART_StopBits = USART_StopBits_1; //停止位
USART_InitStructure.USART_Parity = USART_Parity_No; //无校验
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //无流控
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收发模式
USART_Init(USART1, &USART_InitStructure);
//配置NVIC参数 + 对NVIC初始化
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
//选择USART1的中断源,接收到数据则触发中断
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
//打开串口
USART_Cmd(USART1, ENABLE);
}
//利用串口发送一个字符串
void USART1_SendString(const char *str)
{
while(*str)
{
USART_SendData(USART1,*str++);
while( USART_GetFlagStatus(USART1,USART_FLAG_TXE) == RESET );
}
}
void W25Q128_Config(void)
{
SPI_InitTypeDef SPI_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
//打开SPI1的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
//打开GPIOB端口的时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB,ENABLE);
//选择IO口的复用功能
GPIO_PinAFConfig(GPIOB, GPIO_PinSource3, GPIO_AF_SPI1); //SCK
GPIO_PinAFConfig(GPIOB, GPIO_PinSource4, GPIO_AF_SPI1); //MISO
GPIO_PinAFConfig(GPIOB, GPIO_PinSource5, GPIO_AF_SPI1); //MOSI
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; //复用模式
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5;
GPIO_Init(GPIOB, &GPIO_InitStructure);
//配置CS片选引脚 输出模式
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOB, &GPIO_InitStructure);
//片选引脚,空闲状态为高电平
W25Q128_CS(1);
//配置SPI1 W25Q128存储IC支持模式0和模式3
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //全双工
SPI_InitStructure.SPI_Mode = SPI_Mode_Master; //主模式
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; //8bit数据位
SPI_InitStructure.SPI_CPOL = SPI_CPOL_High; //时钟极性 1
SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge; //时钟相位 1 11 --模式3
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; //软件控制CS片选
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4;
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //高位先出
SPI_Init(SPI1, &SPI_InitStructure);
//使能SPI1
SPI_Cmd(SPI1, ENABLE);
}
//发送一个字节,并且会得到一个字节
uint8_t W25Q128_SendByte(uint8_t byte)
{
/*!< Loop while DR register in not emplty */
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET);
/*!< Send byte through the SPI1 peripheral */
SPI_I2S_SendData(SPI1, byte);
/*!< Wait to receive a byte */
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET);
/*!< Return the byte read from the SPI bus */
return SPI_I2S_ReceiveData(SPI1);
}
//读取设备ID 0x17
uint8_t W25Q128_ReadDeviceID(void)
{
uint8_t device_id = 0;
//1.把片选引脚拉低
W25Q128_CS(0);
//2.MCU发送指令 0x90
W25Q128_SendByte(0x90);
//3.MCU发送地址
W25Q128_SendByte(0x00);
W25Q128_SendByte(0x00);
W25Q128_SendByte(0x00);
//4.MCU提供脉冲,得到厂商ID
W25Q128_SendByte(0xFF);
//5.MCU提供脉冲,得到设备ID
device_id = W25Q128_SendByte(0xFF);
//6.把片选引脚拉高
W25Q128_CS(1);
return device_id;
}
int main()
{
uint8_t buf[128] = {0};
//1.硬件的初始化
USART1_Config(9600);
W25Q128_Config();
//2.读取W25Q128的设备ID
sprintf((char *)buf,"Device ID = %#X\r\n",W25Q128_ReadDeviceID());
USART1_SendString((char *)buf);
while(1)
{
}
}