13.STM32F103C8使用PSRAM64H外扩RAM空间

news2025/1/16 18:05:48

一、简介

最近在调试STM32F103C8驱动墨水屏的实验,在使用过程中,需要使用大内存的RAM需要,由于C8T6的RAM空间只有20KB,而墨水屏的需要的内存为800*480*2/8=93.75KB。

在这里插入图片描述

在网上查了相关的方案,都是使用STM32F103ZE外扩IS62WV51216这种并口SRAM的,使用IS62WV51216需要使用多引脚的STM32F103Zx系列,对于我这种使用背景,不需要上Zx系列,想查找下有没有串口的SRAM方案,但是并没有找到。

网上有成熟的TM32F103Cx系列外扩W25Qxx芯片的方案,但是这是FLASH,不是RAM,也不是我想要的方案。

所以干脆自己做个方案,找到了一块乐鑫的外扩RAM芯片,ESP-PSRAM64H。

在这里插入图片描述

查看其引脚分布,W25Qxx和PSRAM64H的引脚基本一致,都是SPI接口的,并且都支持QSPI。

所以直接使用【WeAct Studio】家的BluePill板子,其自带了W25Qxx接口,但是未焊接,所以直接买了一块,准备焊接PSRAM64进行验证测试。

在这里插入图片描述

使用的STM32库是标准库,为了保证速度,使用的是硬件SPI。

二、使用

2.1 电路图

STM32F103C8核心板W25Qxx电路图

在这里插入图片描述

ESP-PSRAM64的芯片接口

在这里插入图片描述

2.2 代码

2.2.1 SPI

使用的是SPI1的接口,为了保证传输速度,使用硬件SPI而不是软件SPI,所以需要先完成SPI的初始化。相关代码:

#include "spi.h"

void SPI1_Init(void)
{
 	GPIO_InitTypeDef GPIO_InitStructure;
  SPI_InitTypeDef  SPI_InitStructure;

	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE );//PORTA时钟使能 
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1,  ENABLE );//SPI1时钟使能 	

	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //PA5/6/7复用推挽输出 
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA
 	GPIO_SetBits(GPIOA, GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7);  //PA5/6/7上拉
	
	SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;  //设置SPI单向或者双向的数据模式:SPI设置为双线双向全双工
	SPI_InitStructure.SPI_Mode = SPI_Mode_Master;		//设置SPI工作模式:设置为主SPI
	SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;		//设置SPI的数据大小:SPI发送接收8位帧结构
	SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;		//串行同步时钟的空闲状态为高电平
	SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;	//串行同步时钟的第二个跳变沿(上升或下降)数据被采样
	SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;		//NSS信号由硬件(NSS管脚)还是软件(使用SSI位)管理:内部NSS信号有SSI位控制
	SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2;		//定义波特率预分频的值:波特率预分频值为256
	SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;	//指定数据传输从MSB位还是LSB位开始:数据传输从MSB位开始
	SPI_InitStructure.SPI_CRCPolynomial = 7;	//CRC值计算的多项式
	SPI_Init(SPI1, &SPI_InitStructure);  //根据SPI_InitStruct中指定的参数初始化外设SPIx寄存器
 
	SPI_Cmd(SPI1, ENABLE); //使能SPI外设
	
	SPI1_ReadWriteByte(0xff);//启动传输
//	SPI1_SetSpeed(SPI_BaudRatePrescaler_2);//设置为18M时钟,高速模式
}

//SPI 速度设置函数
//SpeedSet:
//SPI_BaudRatePrescaler_2   2分频   
//SPI_BaudRatePrescaler_8   8分频   
//SPI_BaudRatePrescaler_16  16分频  
//SPI_BaudRatePrescaler_256 256分频 
void SPI1_SetSpeed(u8 SPI_BaudRatePrescaler)
{
  assert_param(IS_SPI_BAUDRATE_PRESCALER(SPI_BaudRatePrescaler));
	SPI1->CR1&=0XFFC7;
	SPI1->CR1|=SPI_BaudRatePrescaler;	//设置SPI1速度 
	SPI_Cmd(SPI1, ENABLE); 
}

//SPIx 读写一个字节
//TxData:要写入的字节
//返回值:读取到的字节
u8 SPI1_ReadWriteByte(u8 TxData)
{		
	u8 retry=0;				 	
	while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET) //检查指定的SPI标志位设置与否:发送缓存空标志位
	{
		retry++;
		if(retry>200)return 0;
	}
	SPI_I2S_SendData(SPI1, TxData); //通过外设SPIx发送一个数据
	retry=0;

	while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET) //检查指定的SPI标志位设置与否:接受缓存非空标志位
	{
		retry++;
		if(retry>200)return 0;
	}
	return SPI_I2S_ReceiveData(SPI1); //返回通过SPIx最近接收的数据					    
}

2.2.2 PSRAM64

psram64.c文件

#include "psram64.h"
#include "spi.h"
#include "usart.h"
#include "delay.h"

/*
CS<--->PA4
SCLK<--->PA5
MISO<--->PA6
MOSI<--->PA7
*/

u8 PSRAM64_ReadID(void)
{
	u8 MFID,KGD,EID[6],i;
	PSRAM64_CS=0;
	SPI1_ReadWriteByte(0x9F);//发送读取ID命令	    
	SPI1_ReadWriteByte(0x00);
	SPI1_ReadWriteByte(0x00);
	SPI1_ReadWriteByte(0x00);
	MFID = SPI1_ReadWriteByte(0xFF);
	KGD = SPI1_ReadWriteByte(0xFF);
	for(i=0; i<6; i++)
		EID[i] = SPI1_ReadWriteByte(0xFF);
	PSRAM64_CS=1;
	printf("MDIF=0x%02X\r\n", MFID);
	printf("KGD=0x%02X\r\n", KGD);
	printf("EID=0x%02X%02X%02X%02X%02X%02X\r\n",EID[0],EID[1],EID[2],EID[3],EID[4],EID[5]);
	return KGD;
}

void PSRAM64_Read(u8* pBuffer,u32 ReadAddr,u16 NumByteToRead)
{
	u16 i;
	PSRAM64_CS=0;
	SPI1_ReadWriteByte(PSRAM64_ReadData);       //写读数据命令
	SPI1_ReadWriteByte((u8)((ReadAddr)>>16));  	//写读数据地址  
	SPI1_ReadWriteByte((u8)((ReadAddr)>>8));
	SPI1_ReadWriteByte((u8)ReadAddr);
	for(i=0;i<NumByteToRead;i++)								//读数据
	{
		pBuffer[i]=SPI1_ReadWriteByte(0XFF);
	}
	PSRAM64_CS=1;
}

void PSRAM64_Write(u8* pBuffer,u32 WriteAddr,u16 NumByteToRead)
{
	u16 i;
	PSRAM64_CS=0;
	SPI1_ReadWriteByte(PSRAM64_WriteData);       	//写写数据命令
	SPI1_ReadWriteByte((u8)((WriteAddr)>>16));  	//写写数据地址
	SPI1_ReadWriteByte((u8)((WriteAddr)>>8));
	SPI1_ReadWriteByte((u8)WriteAddr);
	for(i=0;i<NumByteToRead;i++)							//写数据
	{ 
		SPI1_ReadWriteByte(pBuffer[i]);
	}
	PSRAM64_CS=1;
}

void PSRAM64_DataReset(u32 WriteAddr,u16 NumByteToRead)
{
	u16 i;
	PSRAM64_CS=0;
	SPI1_ReadWriteByte(PSRAM64_WriteData);        //写写数据命令
	SPI1_ReadWriteByte((u8)((WriteAddr)>>16));  	//写写数据地址
	SPI1_ReadWriteByte((u8)((WriteAddr)>>8));
	SPI1_ReadWriteByte((u8)WriteAddr);
	for(i=0;i<NumByteToRead;i++)								//复位数据,写0x00
	{ 
		SPI1_ReadWriteByte(0x00);
	}
	PSRAM64_CS=1;
}

void PSRAM64_Reset(void)
{
	PSRAM64_CS=0;
	SPI1_ReadWriteByte(PSRAM64_RESET_ENABLE);   //复位使能
	SPI1_ReadWriteByte(PSRAM64_RESET_CMD);  		//复位
	PSRAM64_CS=1;
}

u8 PSRAM64_Test(void)
{
	u32 addr=0,data,ret=0;
	for(addr=0; addr<PSRAM64_SIZE; addr+=4)
	{
		PSRAM64_Write((u8*)&addr, addr, 4);
		if((addr&0xFFFF)==0)
			printf("write %d/%d\r\n", addr>>16, PSRAM64_SIZE>>16);
	}
	for(addr=0; addr<PSRAM64_SIZE; addr+=4)
	{
		PSRAM64_Read((u8*)&data, addr, 4);
		if(data!=addr)
		{
			printf("read error, addr(0x%08X),data(0x%08X)\r\n", addr, data);
			ret++;
		}
		if((addr&0xFFFF)==0)
			printf("read %d/%d\r\n", addr>>16, PSRAM64_SIZE>>16);
	}
	if(ret==0)
		printf("PSRAM64 测试成功\r\n");
	else
		printf("PSRAM64 测试失败,ret(%d)\r\n",ret);
	return ret;
}

void PSRAM64_Init(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE );//PORTA时钟使能 

	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;  // PA4 推挽 
 	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;  //推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
 	GPIO_Init(GPIOA, &GPIO_InitStructure);
 	GPIO_SetBits(GPIOA, GPIO_Pin_4);
	PSRAM64_CS=1;				//SPI FLASH不选中
	
	SPI1_Init();
	
	delay_ms(10);
	PSRAM64_Reset();
	PSRAM64_ReadID();
	
	PSRAM64_Test();
}

实现了对PSRAM64的测试,按照4字节为单位,对整块芯片的内存进行写,再对写入的数据进行读,对比写入的数据和读出的数据是否一致。

psram64.h文件

#ifndef __PSRAM64_H__
#define __PSRAM64_H__	 
#include "sys.h"

//容量:64Mb=8MB
#define PSRAM64_SIZE 					8388608//8*1024*1024

#define PSRAM64_ReadData			0x03
#define PSRAM64_WriteData			0x02
#define PSRAM64_RESET_ENABLE	0x66
#define PSRAM64_RESET_CMD			0x99


#define	PSRAM64_CS 		PAout(4)  		//PSRAM64的片选信号


u8 PSRAM64_ReadID(void);
void PSRAM64_Read(u8* pBuffer,u32 ReadAddr,u16 NumByteToRead);
void PSRAM64_Write(u8* pBuffer,u32 WriteAddr,u16 NumByteToRead);
void PSRAM64_DataReset(u32 WriteAddr,u16 NumByteToRead);
void PSRAM64_Init(void);
#endif

三、测试结果

3.1 读取ID及写数据

在这里插入图片描述

3.2 读数据

在这里插入图片描述

四、后记

  1. PSRAM64也是支持QSPI的,但是由于目前对QSPI不太熟悉,并且F103也不支持QSPI,后续准备使用QSPI完成对此芯片的读取。
  2. 对于数据手册中的多种读取方式并没有深入了解,不太明白相关概念,后续有机会再继续研究。比如32字节/1K字节突发wrap模式,不太明白是什么意思。也不太清楚低频率和高频率的读是什么区别。
  3. 对于其他外扩串口RAM芯片,比如LY68L6400、APS6404L,也需要完成相关功能的验证。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/678546.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于模拟退火算法的旅行商问题优化(matlab程序)

0.代码链接 基于模拟退火算法的旅行商问题优化&#xff08;matlab程序&#xff09;资源-CSDN文库 1.简述 金属退火是将金属加热到一定温度&#xff0c;保持足够时间&#xff0c;然后以适宜速度冷却(通常是缓慢冷却&#xff0c;有时是控制冷却)的一种金属热处理工艺。模拟退…

ubuntu22.04安装atlas2.0.0

这是本人《数据治理》课程的课程实验&#xff0c;对本人来说非常具有挑战性。 环境: 1、Apache Atlas 2.0 2、JDK 1.8.0_251 3、Maven 3.6.3 因为是课程作业&#xff0c;不用于生产。使用Atlas内嵌Hbase与Solr的部署方式。 在ubuntu裸机上安装&#xff0c;裸机未安装java&#…

ChatGPT | Word表格整体输出

本文来自欧巴Godwin的博客 &#xff0c;引用必须注明出处&#xff01; 用LangChain读取Word和PDF的文字内容是好的&#xff0c;但如果内容包含表格则读取出来的内容完全没法看。用ChatGPT往往是做企业知识库&#xff0c;所以会出现一个场景&#xff0c;即员工想让ChatGPT讲解包…

2、数据库操作语句:多表查询(表employ、department、location)

目录 1、多表查询连接条件 1&#xff09;在employ、department两张表中查询 2&#xff09;若查询语句中出现了多个表中都存在的字段&#xff0c;必须指明此字段所在的表。 3&#xff09;如果表名比较长&#xff0c;SQL语句中多次使用表名&#xff0c;可以给表起别名&#xf…

chatgpt赋能python:Python实现Fibonacci数列

Python实现Fibonacci数列 Fibonacci数列是一个非常经典的数列&#xff0c;定义如下&#xff1a; F ( 0 ) 0 , F ( 1 ) 1 F(0)0, F(1)1 F(0)0,F(1)1 F ( n ) F ( n − 1 ) F ( n − 2 ) F(n)F(n-1)F(n-2) F(n)F(n−1)F(n−2) 也就是说&#xff0c;第n个数等于前两个数之和…

开环模块化多电平换流器仿真(MMC)N=6

模型简介&#xff1a; 运行环境MATLAB2021a 开环模块化多电平换流器仿真&#xff08;MMC&#xff09;N&#xff1d;6&#xff0c;连接负载&#xff0c;采用载波移相调制。 可以得到换流器输出N&#xff0b;1&#xff1d;7电平的相电压波形。可考虑线路阻抗。 子模块采用半桥结…

chatgpt赋能python:用Python实现网络工具——Ping

用Python实现网络工具——Ping 网络是现代社会不可或缺的一部分&#xff0c;而网络工具也是每个程序员的必备技能之一。其中&#xff0c;Ping是最常用的网络工具之一&#xff0c;主要用于测试两台计算机之间的网络连接质量。今天&#xff0c;我们将着重讨论如何用Python实现Pi…

【初识C语言(5)】C语言关键字+define定义常量和宏

文章目录 1. 常见关键字2. 关键字 typedef3. 关键字 static3.1 修饰局部变量3.2 修饰全局变量3.3 修饰函数 4. define 定义常量和宏 1. 常见关键字 C语言提供了丰富的关键字&#xff0c;这些关键字都是语言本身预先设定好的&#xff0c;用户自己是不能创造关键字的。 这里先主要…

SD/StableDiffusion模型,ai绘画部署教程,谷歌云端零成本部署,支持中文

目录 前言 准备前提 说明 开始搭建 1、第一步&#xff0c;下载ipynb脚本文件 2、第二步&#xff0c;上传一键脚本文件到谷歌云盘 3、选择该.ipynb文件--右键--打开方式--关联更多应用 4、输入框搜索Colaboratory找到该应用&#xff0c;安装 5、安装过程中&#xff0c;…

chatgpt赋能python:Python的pow()函数-实现快速幂运算

Python的pow()函数 - 实现快速幂运算 Python是一门高级编程语言&#xff0c;具有简单易学、功能强大、代码可读性高等特点。在Python的内置函数中&#xff0c;有一个很有用的函数叫做pow()。pow()函数可以对数字进行快速幂运算&#xff0c;其实现方法很简单&#xff0c;但是在…

chatgpt赋能python:Python画轨迹图:认识、用法与优势

Python画轨迹图: 认识、用法与优势 Python的数据可视化能力十分强大。画图工具虽然千千万&#xff0c;但其中&#xff0c;轨迹图是同时准确又美观的图形表现。本文将向读者介绍Python画轨迹图的好处、如何使用以及如何在项目合适的地方使用轨迹图。 了解Python轨迹图 为了更…

chatgpt赋能python:Python绘制波浪线教程

Python绘制波浪线教程 Python是一种高级编程语言&#xff0c;它广泛应用于各种领域&#xff0c;如数据分析、Web开发、自动化操作以及人工智能等等。在这篇文章中&#xff0c;我们将介绍如何使用Python来绘制漂亮的波浪线。 简介 绘制波浪线是一种在艺术设计、信息图表、数据…

MySQL压测实战

写作目的 最近看到一句话是MySQL的TPS是4000&#xff0c;这句话是不严谨的&#xff0c;因为没有说服务器的配置。所以自己买了个服务器做了一个压测。希望自己对数据有一个概念。 注意&#xff1a;服务器不同结果不同&#xff0c;结果不具有普适性。 服务器配置 配置参数CPU…

MySQL数据库操作篇2(表的增删查改约束)

在数据类型那篇文章里&#xff0c;笔者曾经提到过约束条件&#xff0c;但也只是简单的提了提&#xff0c;并没有展开来说&#xff0c;约束条件是MySQL为确保数据正确性&#xff0c;安全性&#xff0c;以及倒逼程序员规范操作的一大利器&#xff0c;这篇文章笔者详细谈谈有哪些约…

0019-TIPS-2019-tokyowesterns-gnote : switch(jump) Doubule Fetch

漏洞源码 #include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/fs.h> #include <linux/proc_fs.h> #include <linux/string.h> #include <linux/slab.h> #include <asm/uaccess.h>…

chatgpt赋能python:Python绘制车辆轨迹图

Python绘制车辆轨迹图 在现代交通中&#xff0c;车辆轨迹图是一个广泛应用的技术&#xff0c;它可以被用于道路交通管理&#xff0c;行车安全评估等领域。Python是一种强大的编程语言&#xff0c;它提供了许多绘制数据可视化图表的库。本文将介绍如何使用Python和Matplotlib库…

10.事务消息

4.6 事务消息 4.6.1 流程分析 上图说明了事务消息的大致方案&#xff0c;其中分为两个流程&#xff1a;正常事务消息的发送及提交、事务消息的补偿流程。 1)事务消息发送及提交 (1) 发送消息(half消息)。 (2) 服务端响应消息写入结果。 (3) 根据发送结果执行本地事务(如果写入失…

Golang -> Go 语言快速开发入门

Go 语言快速开发入门 开发一个 hello.go 程序Golang 执行流程分析两种执行流程的方式区别:编译和运行说明 Go 程序开发的注意事项注释行注释多行注释 开发一个 hello.go 程序 package mainimport "fmt"func main() {fmt.Print("hello") }输出: hello对上图…

Cesium 入门

文章目录 一、了解 Cesium二、创建第一个 Cesium 地球三、案例1. Cesium 查看器、场景、实体、数据源介绍2. Cesium 的坐标与转换3. Cesium 相机系统方法一&#xff1a;setView方法二&#xff1a;flyTo方法三&#xff1a;lookAt方法四&#xff1a; viewBoundingSphere 四、案例…

【Leetcode60天带刷】day27回溯算法——39. 组合总和,40.组合总和II,131.分割回文串

​ 题目&#xff1a; 39. 组合总和 给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target &#xff0c;找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 &#xff0c;并以列表形式返回。你可以按 任意顺序 返回这些组合。 candidates 中的 同一…