STM32 SPI小案例软件
spi.h文件
#ifndef __SPI_H
#define __SPI_H
#include <stdio.h>
#include"stm32f10x.h"
/**
* -模拟SPI:是通过CPU生成波形
* PA4:片选信号
* PA5:SCK时钟信号-通用推挽
* PA6:MISO输入信号-通用推挽
* PA6:MISO输入信号-浮空输入
* PA7:MOSI输出信号-通用推挽
*
* SPI工作模式:模式3
* ——极性:1 空闲状态时钟信号处于高电平
* ——相位:1 在偶数沿采样
*/
//定义时钟宏
#define SCK_H (GPIOA->ODR |= GPIO_ODR_ODR5)
#define SCK_L (GPIOA->ODR &= ~GPIO_ODR_ODR5)
//MISO 输入,读输入的值
#define SPI_READ (GPIOA->IDR & GPIO_IDR_IDR6)
//MOSI 输出高低电平
#define SPI_H (GPIOA->ODR |= GPIO_ODR_ODR7)
#define SPI_L (GPIOA->ODR &= ~GPIO_ODR_ODR7)
//延时 构建时钟信号
#define DELAY_US5 delay_us(5)
void SPI_Init(void);
void SPI_Start(void);
void SPI_Stop(void);
uint8_t SPI_SwapByte(uint8_t);
#endif
spi.c文件
#include "spi.h"
void SPI_Init(void)
{
// 使能GPIOA
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;
// 配置引脚工作模式
GPIOA->CRL |= GPIO_CRL_MODE4 | GPIO_CRL_MODE5 | GPIO_CRL_MODE7;
GPIOA->CRL &= ~(GPIO_CRL_CNF4 | GPIO_CRL_CNF5 | GPIO_CRL_CNF7);
// 浮空输入
GPIOA->CRL &= ~GPIO_CRL_MODE6;
GPIOA->CRL &= ~GPIO_CRL_CNF6;
GPIOA->CRL |= GPIO_CRL_CNF6_0;
// 片选信号
GPIOA->ODR |= GPIO_ODR_ODR4;
// 因为采用模式3,所以时钟信号拉高
GPIOA->ODR |= GPIO_ODR_ODR5;
DELAY_US5;
}
void SPI_Start(void)
{
GPIOA->ODR &= ~GPIO_ODR_ODR4;
}
void SPI_Stop(void)
{
GPIOA->ODR |= GPIO_ODR_ODR4;
}
uint8_t SPI_SwapByte(uint8_t byte)
{
for (uint8_t i = 0; i < 8; i++)
{
uint8_t msb = byte & 0x80;
SCK_L;
if (msb)
{
SPI_H;
}
else
{
SPI_L;
}
DELAY_US5;
SCK_H;
byte<<=1;
if(SPI_READ){
byte |= 0x1;
}
DELAY_US5;
}
return byte;
}
w25q32.h文件
#ifndef __W25Q32_H
#define __W25Q32_H
#include "stm32f10x.h"
#include <stdio.h>
#include "spi.h"
/**
* 1.W25Q32初始化
* - 就是SPI的初始化
*
* 2.读取设备信息
* - 指令:0x9F
* - 工厂id
* - 设备id
* - 第一交换确定指令 - 发0x9F
* - 第二次交换返回工厂id - 相当于是读,所以发什么不重要
* - 第三次交换返回设备id高8位
* - 第四次交换返回设备id低8位
*
* 3.写使能
* - 指令:0x06
* 4.写失能
* - 指令:0x04
*
* 5.等繁忙状态-查询状态寄存器
* - 指令:0x05
* - 寄存器S0,1繁忙0空闲
* - 等待空闲 while()
*
*
* 6.擦除sector段
* - 0X ** ** ** **
* - 内存地址的格式,8位的块+8位的段+8位的页+8位页内部数据地址
* - 发起指令前,需要发送写使能
* - 块、段的顺序和地址之间关系
* - block:15 -> 0x0Fxxxxxx
* - block:31 -> 0x1F
* - 序号 * 0x01000000
* - 15 * 0x01000000 = 0x0f0000000
* - 15 * 0x01000000 = 0x1f0000000
*
* - sector:15 -> 0x**0F****
*
*
* 7.写数据
* - 指令:0x02
* - 内存地址 0x** ** ** **
* - 写使能
* - 设置地址(块、段、页)
* - 交换数据
* - 写失能
*
* 8.读数据
* - 指令:0x03
* - 内存地址 0x** ** ** **
* - 写使能
* - 设置地址(块、段、页)
* - 交换数据
* - 写失能
*/
void W25Q32_Init(void);
void W25Q32_ReadID(uint8_t *mid, uint16_t *did);
void W25Q32_WriteEnable(void);
void W25Q32_WriteDisable(void);
void W25Q32_WaitNotBusy(void);
void W25Q32_SectorErase(uint8_t block, uint8_t sector);
void W25Q32_Write(uint8_t block, uint8_t sector, uint8_t page, uint8_t *data, uint16_t len);
void W25Q32_Read(uint8_t block, uint8_t sector, uint8_t page, uint8_t *data, uint16_t len);
#endif
w25q32.c文件
#include "w25q32.h"
void W25Q32_Init(void) {
SPI_Init();
}
void W25Q32_ReadID(uint8_t *mid, uint16_t *did) {
// 启动SPI
SPI_Start();
// 交换数据
// 发指令
SPI_SwapByte(0x9F);
// 接收工厂id
*mid = SPI_SwapByte(0xFF);
// 接收设备id-高位
*did |= SPI_SwapByte(0xFF);
*did <<= 8;
// 接收设备id-低位
*did |= SPI_SwapByte(0xFF);
// 停止SPI
SPI_Stop();
}
void W25Q32_WriteEnable(void) {
// 启动SPI
SPI_Start();
// 交换数据
// 发指令
SPI_SwapByte(0x06);
// 停止SPI
SPI_Stop();
}
void W25Q32_WriteDisable(void) {
// 启动SPI
SPI_Start();
// 交换数据
// 发指令
SPI_SwapByte(0x04);
// 停止SPI
SPI_Stop();
}
void W25Q32_WaitNotBusy(void) {
// 启动SPI
SPI_Start();
// 交换数据
// 发指令
SPI_SwapByte(0x05);
// 状态检测
// 只要获取的值为1则处于等待状态,0退出
while(SPI_SwapByte(0xFF) & 0x01);
// 停止SPI
SPI_Stop();
}
void W25Q32_SectorErase(uint8_t block, uint8_t sector) {
// 确定不繁忙
W25Q32_WaitNotBusy();
// 写使能
W25Q32_WriteEnable();
// 启动SPI
SPI_Start();
// 交换数据
// 发指令
SPI_SwapByte(0x20);
// 将序号转换为地址
// 被擦除段的起始地址
uint32_t addr = block * 0x01000000 + sector * 0x00010000;
SPI_SwapByte(addr >> 24 & 0xff); // block
SPI_SwapByte(addr >> 16 & 0xff); // sector
SPI_SwapByte(addr >> 8 & 0xff); // page
// 停止SPI
SPI_Stop();
// 写失能
W25Q32_WriteEnable();
}
void W25Q32_Write(uint8_t block,
uint8_t sector,
uint8_t page,
uint8_t *data,
uint16_t len) {
// 确定不繁忙
W25Q32_WaitNotBusy();
// 写使能
W25Q32_WriteEnable();
// 启动SPI
SPI_Start();
// 交换数据
// 发指令
SPI_SwapByte(0x02);
// 将序号转换为地址
// 被擦除段的起始地址
uint32_t addr = block * 0x01000000 + sector * 0x00010000 + page *0x00000100;
SPI_SwapByte(addr >> 24 & 0xff); // block
SPI_SwapByte(addr >> 16 & 0xff); // sector
SPI_SwapByte(addr >> 8 & 0xff); // page
// 交换数据
for(uint16_t i=0; i<len; i++) {
SPI_SwapByte(data[i]);
}
// 停止SPI
SPI_Stop();
// 写失能
W25Q32_WriteEnable();
}
void W25Q32_Read(uint8_t block, uint8_t sector, uint8_t page, uint8_t *data, uint16_t len) {
// 确定不繁忙
W25Q32_WaitNotBusy();
// 写使能
W25Q32_WriteEnable();
// 启动SPI
SPI_Start();
// 交换数据
// 发指令
SPI_SwapByte(0x03);
// 将序号转换为地址
// 被擦除段的起始地址
uint32_t addr = block * 0x01000000 + sector * 0x00010000 + page *0x00000100;
SPI_SwapByte(addr >> 24 & 0xff); // block
SPI_SwapByte(addr >> 16 & 0xff); // sector
SPI_SwapByte(addr >> 8 & 0xff); // page
// 交换数据
for(uint16_t i=0; i<len; i++) {
data[i] = SPI_SwapByte(0xFF);
}
// 停止SPI
SPI_Stop();
// 写失能
W25Q32_WriteEnable();
}
————————————————
版权声明:本文为CSDN博主「~」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/2302_77824372/article/details/158315376
更多推荐
所有评论(0)