以 CH549 为例的 51 教程 | 图文 6. 通信:UART 和 SPI(下)

MCU Xy ⋅ 于 2021-08-19 15:40:18 ⋅ 269 阅读

:card_index: 点此回到教程主索引

目录

SPI 介绍

连线

SPI 驱动的 OLED 屏幕具体代码解析

SPI 通信小实验

SPI 介绍

SPI 是串行外设接口的缩写,是一种高速的,全双工的,同步的,串行通讯总线。

高速指的是传输速率普遍大于i2c和uart,

全双工指的是同时可以进行发送和接受,

同步指的是spi中有时钟线,无论输入输出都需要根据其中的时钟信号,

SPI与uart相比,最大的区别是spi是以主从的方式工作的,通常情况下拥有一个主设备和一个或多个从设备,至少需要四根线来完成spi通讯(单向传输的话最少可以三根)
1_四线

MOSI - 主设备数据输出,从设备数据输入

MISO - 主设备数据输入,从设备数据输出

SCLK - 时钟信号,由主设备产生,正是上文所说的spi的同步特性。

CS - 片选使能信号,由主设备产生,只有使能了一个从机获得的cs,从机才可以从主机那接受信息,或给主机发送信息。

2_举例
举个例子,当cs1使能的时候则从机1则可以从MOSI那里接受数据或可以通过MISO发送数据。

连线

根据数据手册和屏幕上各个引脚对应关系,将屏幕与开发板在面包板上连接起来。

3_连线

基础薄弱的同学可以像这样抄下作业:

4_作业

SPI 驱动的 OLED 屏幕具体代码解析

整段代码调用ch549官方提供的spi库并借鉴了oled屏幕厂家提供的stc89c51的例程

main.c

/**
  ******************************************************************
  * @file    main.c
  * @author  甜鱼酱,xy
  * @version V1.0
  * @date    2021-5-19
  * @brief   SPI
  ******************************************************************
  * @attention
  * verimake 用于ch549例程使用SPI驱动oled屏幕
  *   oled引脚接线表
  *   CS     P1_4 //CS片选
  *   RST    P3_5 //LED复位
  *   DC     P2_7 //数据/命令控制
  *   D0     P1_7 //SCLK时钟信号 
  *   D1     P1_5 //MOSI数据
  ******************************************************************
  */
#include <CH549_sdcc.h>     //ch549的头文件,其中定义了单片机的一些特殊功能寄存器 
#include <CH549_OLED.h>     //其中有驱动屏幕使用的函数
#include <CH549_BMP.h>      //用于显示图片的头文件
#include <CH549_DEBUG.h>    //CH549官方提供库的头文件,定义了一些关于主频,延时,串口设置,看门口,赋值设置等基础函数
#include <CH549_SPI.h>      //CH549官方提供库的头文件,定义了一些关于SPI初始化,传输数据等函数
 /********************************************************************
* TIPS:
*   oled引脚接线表
*   CS     P1_4 //CS片选
*   RST    P3_5 //LED复位
*   DC     P2_7 //数据/命令控制
*   D0     P1_7 //SCLK时钟信号 
*   D1     P1_5 //MOSI数据
*********************************************************************/

 int main(void)
 {
    CfgFsys( );
    mDelaymS(20);                                                              //调整主频,建议稍加延时等待内部时钟稳定
    SPIMasterModeSet(3);                                                       //SPI主机模式设置,模式3
    SPI_CK_SET(12);                                                            //设置spi sclk 时钟信号为12分频
    OLED_Init();                                                                     //初始化OLED  
    OLED_Clear();                                                              //将oled屏幕上内容清除
    setFontSize(16);                                                           //设置文字大小
    OLED_ShowString(0,2,"Please follow   Verimake!");
    while(1);
}

更改OLED屏幕上的内容步骤:

1.使用OLED_Clear()函数清楚原oled屏幕上的内容。

2.使用setFontSize()函数设置字符的大小。

3.使用OLED_ShowString()函数设置字符的位置与内容

CH549_SPI.c

/********************************** (C) COPYRIGHT *******************************
* File Name          : 官方spi库
* Author             : WCH
* Version            : V1.0
* Date               : 2018/08/23
* Description        : CH549 SPI主、从模式接口函数
注:片选有效时,从机会自动加载SPI0_S_PRE的预置值到发送移位缓冲区,所以最好可以在片选
有效前向SPI0_S_PRE寄存器写入预发值,或者在主机端丢弃首个接收字节,发送时注意主机会先
取走SPI0_S_PRE里面的值产生一个S0_IF_BYTE中断。
如果片选从无效到有效,从机首先进行发送的话,最好把输出的首字节放到SPI0_S_PRE寄存器中;
如果已经处于片选有效的话,数据数据使用SPI0_DATA就可以
*******************************************************************************/
#include <CH549_SPI.h>
#pragma  NOAREGS
/*******************************************************************************
* Function Name  : SPIMasterModeSet( UINT8 mode )
* Description    : SPI主机模式初始化
* Input          : UINT8 mode
* Output         : None
* Return         : None
*******************************************************************************/
void SPIMasterModeSet(UINT8 mode)
{
    SCS = 1;
    P1_MOD_OC &= ~(bSCS|bMOSI|bSCK);
    P1_DIR_PU |= (bSCS|bMOSI|bSCK);                                            //SCS,MOSI,SCK设推挽输出
    P1_MOD_OC |= bMISO;                                                        //MISO 上拉输入
    P1_DIR_PU |= bMISO;
    SPI0_SETUP = 0;                                                           //Master模式,高位在前
    if(mode == 0)
    {
        SPI0_CTRL = (bS0_MOSI_OE|bS0_SCK_OE);                                   //模式0
    }
    else if(mode == 3)
    {
        SPI0_CTRL = (bS0_MOSI_OE|bS0_SCK_OE|bS0_MST_CLK);                       //模式3
    }
}
/*******************************************************************************
* Function Name  : CH549SPIMasterWrite(UINT8 dat)
* Description    : CH549硬件SPI写数据,主机模式
* Input          : UINT8 dat   数据
* Output         : None
* Return         : None
*******************************************************************************/
void CH549SPIMasterWrite(UINT8 dat)
{
    SPI0_DATA = dat;
    while(S0_FREE == 0)
    {
        ;    //等待传输完成
    }
}
/*******************************************************************************
* Function Name  : CH549SPIMasterRead( )
* Description    : CH549硬件SPI0读数据,主机模式
* Input          : None
* Output         : None
* Return         : UINT8 ret
*******************************************************************************/
UINT8 CH549SPIMasterRead()
{
    SPI0_DATA = 0xff;
    while(S0_FREE == 0)
    {
        ;
    }
    return SPI0_DATA;
}
/*******************************************************************************
* Function Name  : SPISlvModeSet( )
* Description    : SPI从机模式初始化
* Input          : None
* Output         : None
* Return         : None
*******************************************************************************/
void SPISlvModeSet( )
{
    P1_MOD_OC &= ~(bSCS|bMOSI|bSCK);                                          //SCS,MOSI,SCK 浮空输入
    P1_DIR_PU &= ~(bSCS|bMOSI|bSCK);
    P1_MOD_OC &= ~bMISO;                                                      //MISO推挽输出
    P1_DIR_PU |= bMISO;
    SPI0_S_PRE = 0x66;                                                        //预置值,任意值
    SPI0_SETUP = bS0_MODE_SLV;                                                //Slv模式,高位在前
    SPI0_CTRL = bS0_MISO_OE;                                                  //MISO 输出使能
#ifdef SPI_INTERRUPT
    SPI0_SETUP |= bS0_IE_FIRST | bS0_IE_BYTE;
    IE_SPI0 = 1;
    EA = 1;
#endif
}
/*******************************************************************************
* Function Name  : CH549SPISlvWrite(UINT8 dat)
* Description    : CH549硬件SPI写数据,从机模式
* Input          : UINT8 dat   数据
* Output         : None
* Return         : None
*******************************************************************************/
void CH549SPISlvWrite(UINT8 dat)
{
    SPI0_DATA = dat;
    while(S0_FREE==0)
    {
        ;
    }
}
/*******************************************************************************
* Function Name  : CH549SPISlvRead( )
* Description    : CH549硬件SPI0读数据,从机模式
* Input          : None
* Output         : None
* Return         : UINT8 ret
*******************************************************************************/
UINT8 CH549SPISlvRead()
{
    while(S0_FREE == 0)
    {
        ;
    }
    return SPI0_DATA;
}
#ifdef SPI_INTERRUPT
/*******************************************************************************
* Function Name  : SPIInterrupt(void)
* Description    : SPI 中断服务程序
*******************************************************************************/
void SPIInterrupt( void ) interrupt INT_NO_SPI0 using 1        //SPI中断服务程序,使用寄存器组1
{
    UINT8 dat;
    dat = CH549SPISlvRead();
    CH549SPISlvWrite(dat^0xFF);
#if DE_PRINTF
    printf("Read#%02x\n",(UINT16)dat);
#endif
}
#endif

以上为沁恒官方提供的spi库,以下为相比较为重要的函数:

5_重要函数

CH549SPIMasterWrite(UINT8 dat) 用于ch549作为主机使用mosi输出数据给从机。

CH549SPIMasterRead() 用于ch549作为主机使用miso读取从机发送数据。

同样ch549也可以作为spi的从机,库中也有相应的函数。

CH549单片机使用spi读取数据和uart一样可以通过中断来读取的,并不会影响其他代码的运行。

6_中断

编译下载进开发板,即可看到:

7_现象

SPI 通信小实验

  1. 将按键连接引脚p1.0和gnd。
    8_按键
  2. 将main例程进行以下修改:

    /**
    ******************************************************************
    * @file    main.c
    * @author  甜鱼酱,xy
    * @version V1.0
    * @date    2021-5-19
    * @brief   SPI
    ******************************************************************
    * @attention
    * verimake 用于ch549例程使用SPI驱动oled屏幕
    *   oled引脚接线表
    *   CS     P1_4 //CS片选
    *   RST    P3_5 //LED复位
    *   DC     P2_7 //数据/命令控制
    *   D0     P1_7 //SCLK时钟信号 
    *   D1     P1_5 //MOSI数据
    ******************************************************************
    */
    #include <CH549_sdcc.h>     //ch549的头文件,其中定义了单片机的一些特殊功能寄存器 
    #include <CH549_OLED.h>     //其中有驱动屏幕使用的函数
    #include <CH549_BMP.h>      //用于显示图片的头文件
    #include <CH549_DEBUG.h>    //CH549官方提供库的头文件,定义了一些关于主频,延时,串口设置,看门口,赋值设置等基础函数
    #include <CH549_SPI.h>      //CH549官方提供库的头文件,定义了一些关于SPI初始化,传输数据等函数
    /********************************************************************
    * TIPS:
    *   oled引脚接线表
    *   CS     P1_4 //CS片选
    *   RST    P3_5 //LED复位
    *   DC     P2_7 //数据/命令控制
    *   D0     P1_7 //SCLK时钟信号 
    *   D1     P1_5 //MOSI数据
    *********************************************************************/
    #define key1 P1_0     //将单片机的P1.0端口定义为key1
    
    int main(void)
    {
    CfgFsys( );
    mDelaymS(20);                                                              //调整主频,建议稍加延时等待内部时钟稳定
    SPIMasterModeSet(3);                                                       //SPI主机模式设置,模式3
    SPI_CK_SET(12);                                                            //设置spi sclk 时钟信号为12分频
    OLED_Init();                                                                     //初始化OLED  
    OLED_Clear();                                                              //将oled屏幕上内容清除
    setFontSize(16);                                                           //设置文字大小
    OLED_ShowString(0,2,"Please follow   Verimake!");
    while(1)
    {
      if(key1==0)
          {
            OLED_DC = 0;//OLED_DC即p2.7,可选择对oled屏幕传输的数据是用于显示在屏幕上or控制屏幕,赋值为0为选择控制屏幕,赋值为1为选择传输显示在屏幕上的数据
            OLED_CS = 0;//OLED_CS即p1.4,片选控制位,赋值为0则使能此从机,赋值为1则不使能此从机
            CH549SPIMasterWrite(0xAE); //将0xAE作为命令控制通过spi传输给oled,0xAE对应的命令为清空屏幕内容
        }
    }
    //OLED_DC = 0;
    //OLED_CS = 0;
    //CH549SPIMasterWrite(0xAE); 
    }

    具体修改为:

  3. 添加按键端口的定义
  4. 在while(1)中添加
    if(key1==0)
          {
            OLED_DC = 0;//OLED_DC即p2.7,可选择对oled屏幕传输的数据是用于显示在屏幕上or控制屏幕,赋值为0为选择控制屏幕,赋值为1为选择传输显示在屏幕上的数据
            OLED_CS = 0;//OLED_CS即p1.4,片选控制位,赋值为0则使能此从机,赋值为1则不使能此从机
            CH549SPIMasterWrite(0xAE); //将0xAE作为命令控制通过spi传输给oled,0xAE对应的命令为清空屏幕内容
        }

    含义为如果按下按键则会执行三句代码:

    OLED_DC = 0;//OLED_DC即p2.7,可选择对oled屏幕传输的数据是用于显示在屏幕上or控制屏幕,赋值为0为选择控制屏幕,赋值为1为选择传输显示在屏幕上的数据

    第一句将oled_DC赋值为0,oled_DC即p2.7,此变量赋值的意义在于选择对oled屏幕传输的数据是用于显示在屏幕上or控制屏幕,赋值为0为选择控制屏幕,赋值为1为选择传输显示在屏幕上的数据,此处为控制屏幕。

    OLED_CS = 0;//OLED_CS即p1.4,片选控制位,赋值为0则使能此从机,赋值为1则不使能此从机。

    第二句 将OLED_CS赋值为0,OLED_CS即p1.4,即片选控制位,赋值为0则使能此从机,赋值为1则不使能此从机,此处为使能从机。

    CH549SPIMasterWrite(0xAE); //将0xAE作为命令控制通过spi传输给oled,0xAE对应的命令为清空屏幕内容。

    第三句则是将0xAE作为命令控制通过spi传输给oled屏幕,0xAE对应的命令为清空屏幕内容。

3.将改好的代码编译下载,按下按键之后屏幕上原有的字样消失了,表明我们成功的使用ch549完成了SPI通讯。

成为第一个点赞的人吧 :bowtie:
回复数量: 0
    暂无评论~~
    • 请注意单词拼写,以及中英文排版,参考此页
    • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`, 更多语法请见这里 Markdown 语法
    • 支持表情,使用方法请见 Emoji 自动补全来咯,可用的 Emoji 请见 :metal: :point_right: Emoji 列表 :star: :sparkles:
    • 上传图片, 支持拖拽和剪切板黏贴上传, 格式限制 - jpg, png, gif
    • 发布框支持本地存储功能,会在内容变更时保存,「提交」按钮点击时清空
    Ctrl+Enter