STM32F103 SPI配置(SSD1306)

news/2024/12/24 0:01:12/文章来源:https://www.cnblogs.com/zyly/p/18625327

有关SPI通信协议我们在《通信协议-SPI》已经进行了详细的介绍,因此这一节不再重复介绍。

一、软件/硬件SPI

想要控制STM32产生SPI方式的通讯,可以采用软件模拟或硬件SPI这两种方式。

1.1 软件模拟

所谓软件模拟,即直接使用CPU内核按照SPI协议的要求控制GPIO输出高低电平。

1.2 硬件SPI

硬件SPI是指直接利用STM32芯片中的硬件SPI外设,该硬件SPI外设跟USART串口外设类似,只要配置好对应的寄存器, 外设就会产生标准串口协议的时序。

使用它的SPI外设则可以方便地通过外设寄存器产生SPI协议方式的通讯,如初始化好SPI外设后, 只需要把某寄存器位置1,那么外设就会控制对应的SCKMOSI/MISO线自动进行SPI数据传输,而不需要内核直接控制引脚的电平。

相对来说,硬件SPI直接使用外设来控制引脚,可以减轻CPU的负担。不过使用硬件SPI时必须使用某些固定的引脚作为SCLMOSI/MISO, 软件模拟SPI则可以使用任意GPIO引脚,相对比较灵活。

在本开发板中,由于STM32F103RCT6芯片引脚较少,资源比较紧张, 在设计硬件时不方便使用硬件SPI指定的引脚连接外部设备,所以在控制程序上只能使用软件模拟SPI的方式。

二、OLED128x64SSD1306

2.1 回顾

有关OLED128x64(SSD1306)可以参考《Mini2440裸机开发之SPI(OLED SSD1306)》小节中的介绍;

img

在《Mini2440裸机开发之SPI(OLED SSD1306)》我们介绍了SSD1306的常用命令,以及SPI通信方式。

后续我们又在《linux驱动移植-SPI驱动移植(OLED SSD1306)》中介绍了SPI设备驱动的编写,并以OLED SSD1306作为实现设备。

2.2 硬件接线

SSD1306选定SPI接口方式,SPI引脚定义:

  • CS:片选信号;连接是STM32F103PC0引脚;
  • DC:命令数据选择引脚;连接STM32F103PC1引脚;
    • 0:读写命令;
    • 1:读写数据;
  • RES:模块复位引脚,低电平有效;连接STM32F103PC2引脚;
  • D1MOSI引脚,SPI数据线,主设备输出从设备输入引脚;连接STM32F103PC3引脚;
  • D0SCLK引脚,SPI时钟线;连接STM32F103PC4引脚;
  • VCC:电源正极3.3~5V,连接STM32F103PC5引脚;
  • GND:电源地,连接STM32F103PC6引脚。

三、OLED源码实现

3.1 GPIO初始化

配置PC0~PC6引脚,均配置为通用推挽输出,最大速度2MHZ

//************************************************************12864端口定义*********************************
#define OLED_GND   PCout(6)				 //GND
#define OLED_VCC   PCout(5)				 //VCC
#define OLED_SCL   PCout(4)              //串行时钟线
#define OLED_SDA   PCout(3)              //串行数据线
#define OLED_RST   PCout(2)              //硬复位
#define OLED_DC    PCout(1)              //命令/数据标志 0:读写命令  1:读写数据
#define OLED_CS    PCout(0)              //片选信号/*********************************************************************************		Description:  初始化所用到的引脚*******************************************************************************/
void OLED12864_GPIO_Init(void)         //初始化所用到的引脚
{gpio_init(PC0,GPO_PUSH_PULL_2,HIGH);gpio_init(PC1,GPO_PUSH_PULL_2,HIGH);gpio_init(PC2,GPO_PUSH_PULL_2,HIGH);gpio_init(PC3,GPO_PUSH_PULL_2,HIGH);gpio_init(PC4,GPO_PUSH_PULL_2,HIGH);gpio_init(PC5,GPO_PUSH_PULL_2,HIGH);gpio_init(PC6,GPO_PUSH_PULL_2,LOW);
};

3.2 写命令

写命令需要将DC设置为低电平,然后发送一个字节的命令即可;

/*********************************************************************************		Description:  写命令 DC=0   CS=0 CLK上升沿数据传递 *					  数据从高位开始写入*******************************************************************************/void OLED_Wcmd(u8 cmd) 				  //写命令{u8 i;OLED_CS=0;                       //片选OLED_DC=0;						 //写命令for(i=0;i<8;i++){OLED_SCL=0;if(cmd&0x80)OLED_SDA=1;elseOLED_SDA=0;OLED_SCL=1;cmd <<=1;}OLED_CS=1;						//锁存}

3.3 写数据

写数据需要将DC设置为高电平,然后发送一个字节的数据即可;

/*********************************************************************************		Description:  写数据 DC=1   CS=0 CLK上升沿数据传递 *					  数据从高位开始写入  *******************************************************************************/void OLED_Wdata(u8 data) 			  //写数据{u8 i;OLED_CS=0;                       //片选OLED_DC=1;						 //写数据for(i=0;i<8;i++){OLED_SCL=0;if(data&0x80)OLED_SDA=1;elseOLED_SDA=0;OLED_SCL=1;data <<=1;}OLED_CS=1;						//锁存}

3.4 设置OLED坐标

设置OLED坐标需要使用到两个命令:

  • 设置页地址,使用单字节指令: 0xB0H + A[3:0]
  • 设置列地址,使用单字节指令: 0x00H / 0x10H (低/高)+ A[3:0]

实现如下:

/*****************************************************************************************************	  Description:  OLED12864设置坐标    一个ASCII字符字符占用6*8    6列8行*   		   x :  设置列地址0~0X7F*			   y :  设置页地址0~7 ***************************************************************************************************/ 
void OLED_Pos(u8 x,u8 y)     			  //坐标设定
{OLED_Wcmd(0xB0+y);OLED_Wcmd(((x&0xF0)>>4)|0x10);          //设置列地址高四位OLED_Wcmd((x&0x0F)|0x01);				  //设置列地址低四位
} 

3.5 清屏

清屏需要将所有的页数据清零;

//**************************************清屏*******************************
void OLED_Clear(void)
{u8 x;u8 y;	for(y=0;y<8;y++){OLED_Wcmd(0xB0+y);             //选择页OLED_Wcmd(0x01);OLED_Wcmd(0x10);for(x=0;x<0x80;x++)OLED_Wdata(0x00);		   //每次清1列}
}

3.6 OLED初始化

初始化代码如下所示,这里我们就不深究每个命令的含义了,具体查看数据手册;

/*****************************************************************************************************	  Description:  OLED12864初始化**
**************************************************************************************************/ void OLED12864_Init(void)			  //初始化配置{OLED_SCL=1;OLED_CS=0;OLED_RST=0;delay_ms(50);              //复位OLED_RST=1;OLED_Wcmd(0xAE);            //显示关     OLED_Wcmd(0x00)   ;         //设置列低位地址OLED_Wcmd(0x10);            //设置列高位地址OLED_Wcmd(0x40);            //--set start line address  Set Mapping RAM Display Start Line (0x00~0x3F)OLED_Wcmd(0X81);            //设置对比度   OLED_Wcmd(0xCF);            //值越大 越亮  OLED_Wcmd(0xA1);            //设置列左右反置     0xa0左右反置 0xa1正常OLED_Wcmd(0xC8);            //设置行上下反置     0xc0上下反置 0xc8正常OLED_Wcmd(0xA6);            //设置正常显示OLED_Wcmd(0x20);            //设置页地址模式 (0x00/0x01/0x02)OLED_Wcmd(0x02);            OLED_Wcmd(0x8D);			   //设置电荷磊开关OLED_Wcmd(0x14);			   //电荷磊开OLED_Wcmd(0xA4);            //字符显示开关 0xA4:开  0xA5:关OLED_Wcmd(0xA6);            // 背景色显示开关  0xA6:关   0xA7:开OLED_Wcmd(0xAF);            //显示开 OLED_Clear();               //初始清屏OLED_Pos(0,0);  }

3.7 显示字符

关于字符的显示这里不重复介绍了,具体可以查看《Mini2440裸机开发之LCD编程(GB2312ASCII字库制作)》。

3.7.1 1ASCII字符86
 /*********************************************************************************************************  Functon:      OLED_P6x8Str(u8 x,u8 y,u8 *str)*  Description:   写入一组标准ASCII字符串  *  Parameter  :  显示的位置(x,y),y为页范围0~7,要显示的字符串*        	  x :	设置列地址0~0X7F*			  y :   设置页地址0~7 *                 一个字节占8行6列          *
*********************************************************************************************************/  
void OLED_P6x8Str(u8 x,u8 y,u8 *str)	     //6列8行  一列8位
{u8 i=0;u8 j=0;u8 k=0;      while (str[j]!='\0'){  while((str[j]<0x20)||(str[j]>0x80))	 //当写入的没有对应的点阵时 显示空格str[j]=32;     k =str[j]-32;if(x>121){x=0;y++;}OLED_Pos(x,y);    			           //选中坐标for(i=0;i<6;i++)     OLED_Wdata(ASCII6x8[k][i]);  	  //写入一个字节x+=6;j++;}
}
5.7.2 1ASCII字符168
 /*********************************************************************************************************  Functon:      OLED_P16x8Str(u8 x,u8 y,u8 *str)*  Description:   写入一组标准ASCII字符串  *  Parameter  :  显示的位置(x,y),y为页范围0~7,要显示的字符串*        	  x :	设置列地址0~0X7F*			  y :   设置页地址0~7 *	                一个字节占16行8列*    
*********************************************************************************************************/void OLED_P16x8Str(u8 x,u8 y,u8 *str){u8 i=0;u8 j=0;u8 k=0;while(str[j]!='\0'){while((str[j]<0x20)||(str[j]>0x80))	 //当写入的没有对应的点阵时 显示空格str[j]=32;                         k=str[j]-32;if(x>120)                            //列溢出 写入下一页{x=0;y++;}OLED_Pos(x,y);                       //选中页和列坐标for(i=0;i<8;i++)			         //写入上八行{OLED_Wdata(ASCII16x8[k][i]);  }OLED_Pos(x,y+1);for(i=0;i<8;i++)			         //写入下八行{OLED_Wdata(ASCII16x8[k][i+8]);}x+=8;                               //x坐标右移8位 准备写入下一个字节j++;                                //下一个字符 }
}
3.7.3 1个汉字816
/*********************************************************************************************************  Functon:      OLED_P8x16Chi(u8 x,u8 y,u8 *str)*  Description:   写入一组汉字*  Parameter  :  显示的位置(x,y),y为页范围0~7,要显示的字符串*        	  x :	设置列地址0~0X7F*			  y :   设置页地址0~7 *	                一个字占8行16列*    
*********************************************************************************************************/
void OLED_P8x16Chi(u8 x,u8 y,u8 *str)
{u8 i=0;u8 j=0;u8 k=0;while(str[j]!='\0'){              if(x>120)                            //列溢出 写入下一页{x=0;y++;}OLED_Pos(x,y);                       //选中页和列坐标for(i=0;i<16;i++)			         //写入字{OLED_Wdata(CHINESE8x16[k][i]);  }x+=16;                               //x坐标右移8位 准备写入下一个字节j+=2;                                 //下一个字k++;}
}

3.8 实现功能

这里我们通过OLED输出RTC时间。

3.8.1 main函数实现
int main()
{u8 *time;STM32_Clock_Init(9);         	                //系统时钟初始化STM32_NVIC_Init(2,USART1_IRQn,0,1);		        //串口中断优先级初始化,其中包括中断使能usart_init(USART_1,115200);				        //串口1初始化,波特率115200 映射到PA9 PA10STM32_NVIC_Init(2,RTC_IRQn,0,1);		            //RTC中断优先级初始化,其中包括中断使能while(RTC_Init());                               //RTC初始化time = RTCTime();OLED12864_GPIO_Init();                           //GPIO初始化OLED12864_Init();                                //OLED初始化OLED_P16x8Str(45,0,"OLED");	                    //调用LCD_P8x16Str字符串显示函数,在第0页即第一行的第40列开始,显示字符串“OLED"OLED_P8x16Chi(16,6,"安徽理工大学");while(1){time = RTCTime();OLED_P6x8Str(8,4,time);      //显示当前时间delay_ms(1000);} 
}
3.8.2 测试

编译程序并下载测试,这里我们需要按照2.2节进行硬件接线,测试效果如下;

下图是使用逻辑分析器捕捉到的模拟SPI信号;

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

梦幻神器-起-莫愁铃之恩-1星

1-该任务需要提前准备5个三级药,金创药、佛光舍利子除外。 2-第一场战斗是击败地府守卫弟子,1星难度不大,注意"诡蝠之刑"的反伤即可,中了"诡蝠之刑"的单位可以适当防御。3-击杀地府守卫弟子以后跟着流程走,接下来需要给三个水晶注入灵气,需要先注入中…

我家一次用电超负荷时20A保险丝断了,但16A的空气开关却没有跳闸.

回答一: 转载自:https://zhidao.baidu.com/question/1970736314255432140.html这说明两者的保护时限不同。无论是保险丝还是空气开关,并不电流达到就立即跳,而且有一定的时间延时来积累热量,热量到了才会动作。 16A的空气开关没跳说明电流虽然超过20A,但时间很快,保险丝…

Easysearch Java SDK 2.0.x 使用指南(二)

在 上一篇文章 中,我们介绍了 Easysearch Java SDK 2.0.x 的基本使用和批量操作。本文将深入探讨索引管理相关的功能,包括索引的创建、删除、开关、刷新、滚动等操作,以及新版 SDK 提供的同步和异步两种调用方式。 SDK 的对象构建有两种方式 1. 传统的 Builder 方式 最基础的…

《docker高级篇(大厂进阶):2.DockerFile解析》包括:是什么、DockerFile构建过程解析、DockerFile常用保留字指令、案例、小总结

《docker高级篇(大厂进阶):2.DockerFile解析》包括:是什么、DockerFile构建过程解析、DockerFile常用保留字指令、案例、小总结@目录二、高级篇(大厂进阶)2.DockerFile解析2.1 是什么2.2 DockerFile构建过程解析2.3 DockerFile常用保留字指令2.4案例2.4.1自定义镜像mycento…

按钮修改状态

首先创造函数void updateBaoxiaoSchedule( int id, String schedule, String schedulereason); 及其实现。 SQL语句: @Update("update submit set state=#{state},statereason=#{statereason} where id=#{id} ") void updateSubmit1( @Param("id") int id…

泰山派设备控制(RGB)

泰山派设备系统控制(RGB) 1、进入设备系统 cd /sys/class2、进入RGB灯子系统 cd /sys/class/leds/罗列可操作的设备,可以看到三个设备,“rgb-led-b”,“rgb-led-g”,“rgb-led-r”,分别对应RGB的蓝,绿,红。3、我们对其单一色灯进行操作 cd rgb-led-b4、ls罗列出可操作的…

《计算机组成及汇编语言原理》阅读笔记:p48-p81

《计算机组成及汇编语言原理》学习第 4 天,p48-p81 总结,总计 34 页。 一、技术总结 1.CISC vs RISC p49, complex instruction set computing For example, a complex instruction set computing (CISC) chip may be able to move a large block of data, perhaps a strin…

在VS2022中,“根据模型生成数据库”时报错:已添加了具有相同键的项(或:An item with the same key has already been added)

解决方法:关闭解决方案,打开解决方案所在文件夹,打开以下文件夹:.vs\ConsoleApp7\FileContentIndex,然后删除里面的所有文件,再重新在VS中打开解决方案重新生成即可

超级AI图像放大工具Upscayl:让你的照片细节更清晰,色彩更鲜艳!

前言 Hello大家好,我又来推荐非常好用的AI图片无损放大器,模糊图片秒变高清,Upscayl是一个免费开源的AI图像超分辨率工具。它使用AI模型来通过猜测细节的方式增强图像并提高其分辨率。该工具适用于Linux、macOS和Windows操作系统 安装环境 [名称]:Upscayl [大小]:400.99MB …

全民点评 测试用例Xmind

项目简介:该项目是一款基于用户点评和校园商户信息为主的生活服务平台。项目利用 Redis 的特性解决不同业务场景中的问题,核心工作包括设计实现缓存更新策略,解决缓存相关问题、订单超卖的线程安全问题。 技术架构:SpringBoot + Mybatis - Plus + MySQL + Redis 功能测试:…

NginxUI:界面化管理Nginx的工具

一、NginxUI简介 1.1 NginxUI介绍 Nginx UI 是一个全新的 Nginx 网络管理界面,旨在简化 Nginx 服务器的管理和配置。它提供实时服务器统计数据、ChatGPT 助手、一键部署、Lets Encrypt 证书的自动续签以及用户友好的网站配置编辑工具。 此外,Nginx UI 还提供了在线访问 Nginx…

Codeforces 1842I. Tenzing and Necklace

神仙题。本题解参考官方题解进行编写,并补充了最后比较关键的怎么调整 \(m\)。 题目链接:I - Tenzing and Necklace 题目大意:给定一个环,环上有 \(n\) 个点与 \(n\) 条边,第 \(i\) 条边连接 \(i\) 与 \(i\bmod n +1\),边权为 \(a_i\)。要求断开若干边使得环断为若干段,…