其实各种协议是很重要的,这篇文章就当做我对spi协议的一个整理吧。

必要的spi简介:

https://www.cnblogs.com/zengsf/p/7221207.html?utm_source=itdadao&utm_medium=referral

前几天在网上看到一段关于oled的程序

不过那段程序是用的io口模拟spi来控制oled模块的

我在想stm32本身就有spi为何要用io口来模拟spi协议呢

所以就想自己试着写一写。

首先第一部分是关于stm32的spi引脚:

http://www.eeworld.com.cn/mcu/2015/0615/article_20333.html

SPI1->CS      ------ PA4

SPI1->CLK    ------ PA5 
SPI1->MISO  ------ PA6
SPI1->MOSI  ------ PA7

SPI2->CS      ------ PB12
SPI2->CLK    ------ PB13 
SPI2->MISO  ------ PB14 
SPI2->MOSI  ------ PB15

SPI3->CS      ------ PA15  
SPI3->CLK    ------ PB3 
SPI3->MISO  ------ PB4 
SPI3->MOSI  ------ PB5

对于SPI ,需要打开相关RCC时钟
主模式下
CLK 配置成复用推挽输出
MOSI 配置成复用推挽输出
MISO 配置成富哦那个或带上拉输入
CS若采用硬件则配置成推挽输出,若采用软件模式,则采用普通IO推挽输出即可。

 
根据上面的提示,不如我们就选用spi1吧
http://blog.sina.com.cn/s/blog_66513d610102wv3p.html
知道了引脚,然后我们开始用库函数去配置spi:
spi1的SCLK,MISO,MOSI分别是PA5,PA6,PA7引脚,这几个引脚配置成GPIO_Mode_AF_PP即复用推挽输出。
如果是单主单从,CS引脚可以不配置,都设置成软件模式即可。

引脚配置好了,我们下面进行spi模式的配置。下面的图片仅供参考,具体问题还需具体分析。spi的极性和相位为4中,我们还需要根据实际情况去查看。

配置好了之后,我们就开始写应用了,也就是收发函数,收发函数把数据通过配置好的底层发出去。

对于应用函数,我们应该设置好形参,形参主要是用来保存协议来往的数据的。

这个协议的收发函数有两种(因为这个协议是双工的):读写分开的函数,读写一起的。

读写分开的函数:

void SPI_Ecah_Buffer_Send(u8* pBuffer, u16 NumByteToRead) //发送

{

for(int i = 0; i < NumByteToRead; i++)

{

SPI_Conmunication_SendByte(*pBuffer);

pBuffer++;

}

}

void SPI_Buffer_Receive(u8* pBuffer, u16 NumByteToRead) //接收

{

while (NumByteToRead--)

{

*pBuffer = SPI_Conmunication_SendByte (Dummy_Byte);

pBuffer++;

}

}

 
 
还有一种是读写一体的

void SPI_Ecah_Buffer_Send(u8* str , u8* pBuffer, u16 NumByteToRead)

{

for(int i = 0; i < NumByteToRead; i++)

{

*str = SPI_Conmunication_SendByte(*pBuffer);

pBuffer++;

str++;

}

}

 
 
好了到了这里之后,我们基本上已经把所有的框架写好了,来整理一下用到的库函数
这些函数都是stm32的库里给我的API接口,我们配置好底层之后,就可以用这些函数去编写自己的应用了。
SPI_Cmd(SPI2,ENABLE);//使能SPI外设
void SPI_I2S_SendData(SPI_TypeDef* SPIx, uint16_t Data); //发送数据函数
uint16_t SPI_I2S_ReceiveData(SPI_TypeDef* SPIx) ;//接收数据函数
SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE);//判断数据是否传输完成
 
 
之后我们应该在上面的框架里补充和修正一些细节。
 我们最终的目的还是想让stm32的spi驱动oled,关于oled的细节
https://www.cnblogs.com/wp2312139418/p/5988713.html
上面文章真的很详细。
 

OLED引脚介绍: 这个是oled模块上的几个引脚。我们要把它和spi对应起来。

CS:OLED片选信号

RST:OLED复位端口

DC: 命令/数据选择端口(0:读写命令, 1: 读写数据)

SCLK(D0):串口时钟线

SDIN(D1): 串口数据线

 
 
stm32与OLED屏接口的引脚介绍:

CS————GPIOD3;    spi的片选

RST————GPIOD4;    复位(spi里没有)

DC—————GPIOD5;   表示写数据还是命令。(spi里没有)

D0——————GPIOD6; spi时钟线

D1——————GPIOD7;   spi MOSI,代表oled从这里接收数据,假设单片机是主机,oled屏是从机。

上面接线,蓝色是我推出来的。
 
我们先分析一下上面网友的模拟spi的程序:
不知道为什么,我写到这里的时候,脑子里竟然是老师讲课时的情景,
告诉我,spi有四种模式。我们可以从下面的程序中发现oled适用于哪一种模式。
 
/*    SPI写数据/命令
 *    Mode :O:写命令   1:写数据
 *    data :数据/命令
 *
*/
void SPI_Write(char data, int Mode)
{    
    int i = 0;
    if(Mode)                   //这个是用来区分命令,还是数据的。
    {
        OLED_DC(1);        //DC引脚输入高,表示写数据
    }
    else
    {
        OLED_DC(0);        //DC引脚输入低,表示写命令
    }
    OLED_CS(0);            //CS引脚输入低,片选使能   这里符合spi协议,低电平是选中。
    for(i = 0; i < 8; i++)     //从这句话,我们能判断出,这个spi协议是,8位的, spi分为8帧和16帧
    {
        OLED_D0(0);        //D0引脚输入低
        if(data & 0x80)      //判断传输的数据最高位为1还是0   从这里判断是先传输高位, spi协议有先传高位或先传低位。
        {
            OLED_D1(1);    //D1引脚输入高 
        }
        else
        {
            OLED_D1(0);    //D1引脚输入低
        }
        OLED_D0(1);        //D1引脚输入高    //先准备好数据,然后在让时钟有一个上升沿,也就是上升沿的时候读取数据。
        data <<= 1;        //将数据左移一位
    }
    OLED_DC(0);            //DC引脚输入低
    OLED_CS(1);            //CS引脚输入高,片选失能,  //这个可能是为了防止干扰,
}
 
所以呢。我们要把io口改一下:
 
 OLED屏与stm32接口的引脚介绍:

CS————GPIOD3;    spi的片选

RST————GPIOD4;    复位(spi里没有)

DC—————GPIOD5;   表示写数据还是命令。(spi里没有)

D0——————GPIOD6; spi时钟线

D1——————GPIOD7;   spi MOSI,代表oled从这里接收数据,假设单片机是主机,oled屏是从机。

 
 把上面的一层关系,修改成下面这样:
单片机                        -----oled屏幕

SPI1->CS      ------ PA4----CS

SPI1->CLK    ------ PA5 ----D0
SPI1->MISO  ------ PA6-----D1

SPI1->MOSI  ------ PA7

                             PB1-----RST
                             PB2-----DC
 
 之后,就是spi配置的修正:
也就是上面提到的这个图片:

第一个是全双工,其实没有必要,因为oled屏好像不会返回数据给stm32

第二行是从机,我感觉单片机应该还是主机的好

第三个8帧,这个应该不用改

第四个,这个应该也是低电平,空闲时刻D0 ,,,,,,候选项:SPI_CPOL_High(=1)和SPI_CPOL_Low ( =0)

第五个,感觉应该是第一个跳变沿被采集,,,,,,, 候选项:SPI_CPHA_1Edge (=0) 和SPI_CPHA_2Edge(=1)

第六个,cs片选引脚为软件模式

第七个, 这个是波特率,分频为8,可以但是这个时钟默认应该是36M的。

第八个,这个的确是先传高字节

第九个,这个是CRC校验,实际上是这个赋值是7是没有意义的,至于为什么?

https://blog.csdn.net/kobesdu/article/details/50972273

SPI_CRCPolynomial :这是 SPI 的 CRC 校验中的多项式,若我们使用 CRC 校验
时,就使用这个成员的参数(多项式)来计算 CRC 的值。由于本实验的 Flash 不支持 CRC
校验,所以我们向这个结构体成员赋值为7 实际上是没有意义的。
配置完这些结构体成员后,我们要调用SPI_Init() 函数把这些参数写入寄存器中,实现
SPI 的初始化,然后调用

 
 之后我们把引脚线改一下,然后把spi配置改一下,
然后程序要改
void SPI_Write(char data, int Mode) ,这个函数我们就不再用了。
然后需要重新写一个,用SPI_Conmunication_SendByte(*pBuffer);
这个库函数写一个,按照oled的操作规则写一个应用函数,看来是协议
包含着协议呀,
 
这个我晚上回去测试一下,看看能不能用。
不过改来改去似乎没有觉得有多么简单,也许调用库函数对于移植和后期的维护是很方便的吧。
 
改一下函数:
/*    SPI写数据/命令
 *    Mode :O:写命令   1:写数据
 *    data :数据/命令
 *
*/
void SPI_Write(char data, int Mode)
{    
    int i = 0;
    if(Mode)                   //这个是用来区分命令,还是数据的。
    {
        OLED_DC(1);        //DC引脚输入高,表示写数据
    }
    else
    {
        OLED_DC(0);        //DC引脚输入低,表示写命令
    }
    OLED_CS(0);            //CS引脚输入低,片选使能   这里符合spi协议,低电平是选中。
   SPI_Conmunication_SendByte(data);  //这个是协议部分,不知道这样可以不可以。
    OLED_DC(0);            //DC引脚输入低
    OLED_CS(1);            //CS引脚输入高,片选失能,  //这个可能是为了防止干扰,
}
 
 
先整理一下网友的程序。
然后把我的程序改进去试一下。
 
见我的另一篇博客:oled的一套stm32实验。

oled stm32的spi的更多相关文章

  1. STM32 F4 SPI Accelerometer

    STM32 F4 SPI Accelerometer

  2. STM32的SPI口的DMA读写[原创www.cnblogs.com/helesheng]

    SPI是我最常用的接口之一,连接管脚仅为4根:在常见的芯片间通信方式中,速度远优于UART.I2C等其他接口.STM32的SPI口的同步时钟最快可到PCLK的二分之一,单个字节或字的通信时间都在us以 ...

  3. FPGA作为从机与STM32进行SPI协议通信---Verilog实现 [转]

    一.SPI协议简要介绍 SPI,是英语Serial Peripheral Interface的缩写,顾名思义就是串行外围设备接口.SPI,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用 ...

  4. STM32的SPI问题。

    问题描述: 之前一直使用的单片机是LPC2109,对其SPI很熟悉.基本就是原本拿来稍作修改就用.由于某种原因需要使用STM32,然后设备的驱动是之前写好的,只修改了一些硬件控制端口,由于硬件驱动使用 ...

  5. stm32之SPI通信协议

    SPI (Serial Peripheral interface),顾名思义就是串行外围设备接口.SPI是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为P ...

  6. FPGA作为从机与STM32进行SPI协议通信---Verilog实现

    一.SPI协议简要介绍 SPI,是英语Serial Peripheral Interface的缩写,顾名思义就是串行外围设备接口.SPI,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用 ...

  7. STM32之spi管理模式

    1)sip管理模式分为:硬件管理和软件管理:主要由NSS .SSI.SSM决定: NSS是芯片上一个实实在在的引脚,SSI和SSM是SPI_CR1控制器里的的位. 值得注意的是:NSS分外部引脚和内部 ...

  8. STM32 HAL SPI读取MPU6500的设备ID异常

    1.问题背景 近前,使用STM32F4 HAL库的SPI读取MPU6500出现异常. 现象:读取ID失败,返回0,以为硬件焊接问题,各种排查,最后为了示波器测试方便,把读取ID的函数放到While(1 ...

  9. STM32之SPI时钟相位选择

    SPI的时钟模式分为四种,由SPI_CR1寄存器的两位CPOL,CPHA组合选择. CPOL 如果为1,则时钟的空闲电平为高电平:CPOL 如果为0,则时钟的空闲电平为低电平.空闲电平影响不大. CP ...

随机推荐

  1. [Python] Understand Mutable vs. Immutable objects in Python

    In this lesson, you will learn what mutable and immutable objects are, and the difference between th ...

  2. [ES2017] Iterate over properties of an object with ES2017 Object.entries()

    The Object.entries() function is an addition to the ECMAscript scpec in Es2017. This allows us to it ...

  3. iOS使用push隐藏子页面底部bottom TabBar

    下面两种情况是我在开发过程中遇到的,一种是代码使用pushViewController,还有一种是storyboard直接使用push.之前也查阅了非常多关于隐藏底部tabbar的资料.可是要么使用起 ...

  4. shape-自绘制简单图形

    shape 可以绘制简单的图形,颜色等.它主要就是应用于selector 的一些状态. 本文内容参考自http://www.cnblogs.com/cyanfei/archive/2012/07/27 ...

  5. PDF Adobe Acrobat 9 简体中文专业版(打印店内部的软件)(你懂的!)

    福利 => 每天都推送 欢迎大家,关注微信扫码并加入我的4个微信公众号:   大数据躺过的坑      Java从入门到架构师      人工智能躺过的坑         Java全栈大联盟   ...

  6. ip---查看网络信息

    Linux的ip命令和ifconfig类似,但前者功能更强大,并旨在取代后者. ifconfig属于net-tools.ip属于iproute2 设置一个IP地址,可以使用下列ip命令: ip add ...

  7. readonly&&declare&&unset &&export&&env环境变量

    readonly命令用于定义只读shell变量和shell函数.readonly命令的选项-p可以输出显示系统中所有定义的只读变量. 选项 -f:定义只读函数: -a:定义只读数组变量: -p:显示系 ...

  8. CTF编程题-三羊献瑞(实验吧)解题随记

    题目如下.解题步骤参考的是https://cloud.tencent.com/developer/news/373865中作者的思路. 1.首先,两个四位数相加等于一个五位数,那么这个五位数的第一位必 ...

  9. 使用TCP协议的NAT穿透技术 (转载)

    其实很早我就已经实现了使用TCP协议穿透NAT了,但是苦于一直没有时间,所以没有写出来,现在终于放假有一点空闲,于是写出来共享之. 一直以来,说起NAT穿透,很多人都会被告知使用UDP打孔这个技术,基 ...

  10. 快速排序的期望复杂度O(nlogn)证明。

    快速排序的最优时间复杂度是 \(O(nlogn)\),最差时间复杂度是 \(O(n^2)\),期望时间复杂度是 \(O(nlogn)\). 这里我们证明一下快排的期望时间复杂度. 设 \(T(n)\) ...