SPI通讯 + Arduino Uno + 六针 0.96 OLED + U8g2

1. SPI 通讯

简介

串行外设接口(Serial Peripheral Interface, SPI)是微控制器使用的同步串行数据协议,用于在短距离内快速与一个或多个外围设备通信。它还可以用于两个微控制器之间的通信。

该接口一般使用4条线进行连接:

  • SCK - 主机驱动的串行时钟,用于同步数据传输;
  • MOSI(Master Out Slave In)- Master line,用于Master向Slave发送数据
  • MISO(Master In Slave Out)- Slave line,用于Slave向Master发送数据
  • SS(Slave Select)- 外设选择线,这条线也称为 CS(Chip Select,芯片选择线或简称片选)不同引脚分配给所有外设,输出0代表选取某设备,避免由于线路忙导致的错误传输。

一主多从

SPI 时序和工作模式

在这里插入图片描述

上图中的时序是 SPI 不同的通讯模式,SSEL、SCK、MOSI 信号都由主机控制产生,而 MISO 的信 号由从机产生,主机通过该信号线读取从机的数据。MOSI 与 MISO 的信号只在 SSEL 为低 电平的时候才有效,在 SCK 的每个时钟周期 MOSI 和 MISO 传输一位数据。

SPI 一共有四种通讯模式,它们的主要区别是总线空闲时 SCK 的时钟状态以及数据采样时刻,涉及到两个概念:

时钟极性 CPOL 指 SPI 通讯设备空闲时,SCK 信号线的电平信号(即 SPI 通讯开始前、 NSS 线为高电平时 SCK 的状态)。
CPOL=0 时, SCK 在空闲状态时为低电平,CPOL=1 时,则相反。

时钟相位 CPHA 指数据的采样的时刻,CPHA=0,表示数据采样是在第1个边沿(上升或下降),数据发送在第2个边沿;CPHA=1,表示数据采样是在第2个边沿,数据发送在第1个边沿

根据 CPOL 及 CPHA 的不同状态,SPI 有以下四种工作模式,主机与从机需要工作在相同的模式下才可以正常通讯,实际中采用较多的是“模式 0”与“模式 3”,因为它们都是在上升沿采样数据,不用去在乎时钟的初始电平是什么,只要在上升沿采集数据就行。
在这里插入图片描述
需要注意的是:
我们的主设备能够控制时钟,因为我们的SPI通信并不像UART或者IIC通信那样有专门的通信周期,有专门的通信起始信号,有专门的通信结束信号;所以我们的SPI协议能够通过控制时钟信号线,当没有数据交流的时候我们的时钟线要么是保持高电平要么是保持低电平。

内部工作机制

在这里插入图片描述
SSPSR 是 SPI 设备内部的移位寄存器(Shift Register). 它的主要作用是根据 SPI时钟信号状态, 往 SSPBUF 里移入或者移出数据, 每次移动的数据大小由 Bus-Width 以及 Channel-Width 所决定.

SPI接口在内部硬件实际上是两个简单的移位寄存器,传输的数据为8位,在主器件产生的从器件使能信号和移位脉冲下,按位传输,高位在前,低位在后。

特点

  • 优点
    – 支持全双工通信
    – 通信简单
    – 数据传输速率块

  • 缺点
    – 没有指定的流控制,没有应答机制确认是否接收到数据,所以跟 IIC总线协议比较在数据可靠性上有一定的缺陷。

2. Arduino + u8g2 + oled

  • 材料准备

    u8g2库安装
    arduino uno
    六针 oled 屏幕

  • 接线

    GND:电源地
    VCC:2.2V~5.5V
    SCL(D0):CLK 时钟 (高电平 2.2V~5.5V)
    SDA(D1):MOSI 数据(高电平 2.2V~5.5V)
    RST:复位(高电平 2.2V~5.5V)
    D/C:数据/命令(高电平 2.2V~5.5V)

六针oled屏没有CS接口,其在硬件中直接接地(只有一个SPI从机时CS可以不使用);

此外,Arduino发送指令和数据到 OLED,OLED 并不需要反馈任何数据。因此, OLED 模块是只有 MOSI(SDA)引脚,没 有 MISO 引脚的。

  • 代码
#include <Arduino.h>
#include <SPI.h>
#include <U8g2lib.h>

// 前缀_U8G2
// 驱动芯片_SSD1306
// 屏幕尺寸_128X64
// 显示器名称_NONAME
// 缓存大小_F/1/2
// 通信协议4W_SW_SPI-四线软件模拟SPI总线
U8G2_SSD1306_128X64_NONAME_F_4W_SW_SPI u8g2(U8G2_R0, /* clock=*/ 11, /* data=*/ 10,
                              /* cs=*/ 12, /* dc=*/ 8, /* reset=*/ 9);

void setup(void) 
{
  u8g2.begin();   //选择U8G2模式,或者U8X8模式
}

void loop(void) 
{
  u8g2.clearBuffer();         // 清除内部缓冲区
  u8g2.setFont(u8g2_font_ncenB08_tr); // choose a suitable font
  u8g2.drawStr(0,10,"Hello World!");  // write something to the internal memory
  u8g2.drawStr(0,20,"OLED TEST!");  // write something to the internal memory
  u8g2.drawStr(0,30,"Welcome to U8G2!");  // write something to the internal memory
  u8g2.sendBuffer();          // transfer internal memory to the display
  delay(1000);  
}

在这里插入图片描述

参考资料:

SPI协议和OLED详解及裸机程序开发分析(软件结构模拟SPI)
SPI工作方式简介
SPI通信协议(SPI总线)学习
u8g2 官方文档
arduino驱动4线/6线OLED屏幕(I2C/SPI通讯)
Auduino + EEPROM 官方文档
SPI总线协议及SPI时序图详解
嵌入式STM32 HAL_IIC EEPROM
手撸SPI接口oled屏幕驱动(stm32)

  • 2
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个简单的示例代码,用于驱动7线OLED显示屏(例如SSD1306)的STM32F1系列微控制器。 ```c #include "stm32f10x.h" #include "stm32f10x_spi.h" #include "stm32f10x_gpio.h" // OLED引脚定义 #define OLED_CS_PIN GPIO_Pin_4 #define OLED_DC_PIN GPIO_Pin_5 #define OLED_RST_PIN GPIO_Pin_6 #define OLED_CLK_PIN GPIO_Pin_5 #define OLED_MOSI_PIN GPIO_Pin_7 // OLED引脚端口定义 #define OLED_CS_PORT GPIOA #define OLED_DC_PORT GPIOA #define OLED_RST_PORT GPIOA #define OLED_CLK_PORT GPIOA #define OLED_MOSI_PORT GPIOA // OLED命令和数据定义 #define OLED_COMMAND 0x00 #define OLED_DATA 0x01 // OLED初始化函数 void OLED_Init(void) { // 初始化引脚 GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Pin = OLED_CS_PIN | OLED_DC_PIN | OLED_RST_PIN | OLED_CLK_PIN | OLED_MOSI_PIN; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_Init(OLED_CS_PORT, &GPIO_InitStructure); // 初始化SPI SPI_InitTypeDef SPI_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE); SPI_InitStructure.SPI_Direction = SPI_Direction_1Line_Tx; SPI_InitStructure.SPI_Mode = SPI_Mode_Master; SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low; SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge; SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4; SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; SPI_Init(SPI1, &SPI_InitStructure); SPI_Cmd(SPI1, ENABLE); } // OLED发送命令或数据 void OLED_SendByte(uint8_t data, uint8_t dc) { // 设置DC引脚 if (dc == OLED_COMMAND) GPIO_ResetBits(OLED_DC_PORT, OLED_DC_PIN); else GPIO_SetBits(OLED_DC_PORT, OLED_DC_PIN); // 通过SPI发送数据 GPIO_ResetBits(OLED_CS_PORT, OLED_CS_PIN); SPI_I2S_SendData(SPI1, data); while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_BSY) == SET) ; GPIO_SetBits(OLED_CS_PORT, OLED_CS_PIN); } // OLED清屏函数 void OLED_Clear(void) { for (int i = 0; i < 8; i++) { OLED_SendByte(0xb0 + i, OLED_COMMAND); // 设置页地址(0~7) OLED_SendByte(0x00, OLED_COMMAND); // 设置显示位置—列低地址 OLED_SendByte(0x10, OLED_COMMAND); // 设置显示位置—列高地址 for (int j = 0; j < 128; j++) { OLED_SendByte(0x00, OLED_DATA); // 清空该页数据 } } } int main(void) { // 初始化OLED OLED_Init(); // 清屏 OLED_Clear(); while (1) { // 程序主循环 } } ``` 请注意,这只是一个简单的示例代码,具体的驱动程序可能因OLED型号和连接方式而有所不同。您需要根据自己的具体情况进行修改和适配。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值