DSP28335基础教程——I2C通信实验(OLED显示控制)

Posted Sk Electronics

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了DSP28335基础教程——I2C通信实验(OLED显示控制)相关的知识,希望对你有一定的参考价值。

0 前言

本期实验目标:采用硬件I2C模块与OLED进行通信,完成显示控制。

目前发现28335比较缺乏关于I2C模块的实用例程,许多新手在配置使用该模块比较难上手,走弯路。那么通过该例程,大家则可以快速学会使用I2C的基本功能,避免踩坑,让I2C模块运行起来吧!

本节仍然将分为硬件部分、软件部分和实验展示三个方面进行介绍。

1 硬件部分

我们采用中景园电子的0.96寸OLED模块,它的控制芯片是SSD1306,如图1所示。
在这里插入图片描述

图1 OLED实物图

由于该OLED模块默认是4线SPI的,为了换成支持I2C方式,从图中我们把电阻R1、R4、R6、R7焊上4.7k的电阻,R3电阻去掉,R8电阻用0欧短接起来即可。

表1 OLED管脚定义

在这里插入图片描述
表1给出OLED模块的管脚定义,其中OLED在I2C通信方式中,管脚DC不再是用于数据和命令选择,它是用来作于从机地址。在SSD1306的数据手册中,图2表示了它的7位从机地址与SA0有关,而SA0又是DC管脚的电平状态,在这里我们DC接地,则7位从机地址为0x3C。

注意:在硬件I2C里,R/W#是会自动发送的,我们不能把这一位当做从机地址的一部分,不清楚的朋友可能会认为从机地址是0x78(R/W#=0),但这是错的。因此我们在看数据手册应要注意给出的地址有没有包括R/W#!
在这里插入图片描述

图2 OLED的I2C从机地址定义

不懂I2C的具体工作原理的,百度搜一下非常多,这里不在赘述。接下来是与DSP28335的连线,图3给出硬件连线图。
在这里插入图片描述

图3 硬件连线图

2 软件部分

程序分为I2C的IO口和功能配置、RES输出管脚配置和OLED控制部分。(注意:查看代码时双击点进去看,否则会内容不全)。
bsp_i2c.c(I2C配置)

/**
 *  ********************************************************************************************
 *  @file                 bsp_i2c.c
 *  @file                 SK Electronics
 *  @version           V1.0
 *  @date               2021-xx-xx
 *  @brief               I2C通信应用函数接口
 *  *******************************************************************************************
 *  @attention
 *  实验平台:SK-F28335Mini   核心板
 *  CSDN博客:https://blog.csdn.net/weixin_46556696
 *  淘宝:https://shop409670932.taobao.com
 */
#include "bsp_i2c.h"

/**
 *  @brief               IIC模块初始化,设置IIC模块的功能引脚以及设置IIC的工作方式
 *  @parameter              无
 *  @return_value           无
 */
void Init_I2CA(void)
{
    I2caRegs.I2CMDR.all = 0x0000;   // 复位IIC

    EALLOW;
    GpioCtrlRegs.GPBPUD.bit.GPIO32 = 0;    // 使能(SDAA)上拉
    GpioCtrlRegs.GPBPUD.bit.GPIO33 = 0;    //  使能 (SCLA)上拉
    GpioCtrlRegs.GPBQSEL1.bit.GPIO32 = 3;  // 同步 (SDAA)
    GpioCtrlRegs.GPBQSEL1.bit.GPIO33 = 3;  // 同步 (SCLA)
    GpioCtrlRegs.GPBMUX1.bit.GPIO32 = 1;   // 配置 GPIO32为 SDAA
    GpioCtrlRegs.GPBMUX1.bit.GPIO33 = 1;   // 配置GPIO33 为SCLA

    // 预分频——时钟模块的频率
    I2caRegs.I2CPSC.all = 14;       // 预分频   IIC模块时钟需设置为7-12MHz,本实验设置为 (150/15 = 10MHz)
    I2caRegs.I2CCLKL = 10;   //时钟低电平时间值
    I2caRegs.I2CCLKH = 5;   //时钟高电平时间值
    I2caRegs.I2CIER.all = 0x00;      // Enable SCD & ARDY interrupts

    I2caRegs.I2CFFTX.all = 0x0000;   // Disable FIFO mode and TXFIFO
    I2caRegs.I2CFFRX.all = 0x0000;   // Disable RXFIFO, clear RXFFINT,
    I2caRegs.I2CMDR.all = 0x0020;   // IIC准备就绪
    EDIS;

}

/**
 *  @brief              IIC模块发送准备OK
 *  @parameter              无
 *  @return_value           无
 */
Uint16  I2C_xrdy()
{
    Uint16  t;
    t = I2caRegs.I2CSTR.bit.XRDY;   // IIC模块发送准备OK
    return t;
}

/**
 *  @brief              IIC模块接收准备OK
 *  @parameter              无
 *  @return_value           无
 */
Uint16  I2C_rrdy()
{
    Uint16  t;
    t = I2caRegs.I2CSTR.bit.RRDY;   //  IIC模块接收准备OK
    return t;
}

/**
 *  @brief                  IIC写数据
 *  @parameter              SlaveAddress:从机地址;  RomAddress:寄存器地址; number:写入数据的字节数;  *Wdata:写入数据的地址
 *  @return_value           状态标志
 */
Uint16 WriteData( Uint16 SlaveAddress, Uint16 RomAddress,Uint16 number, Uint8 *Wdata)
{
   Uint16 i;
   if (I2caRegs.I2CSTR.bit.BB == 1)
   {
      return I2C_BUS_BUSY_ERROR;   //返回总线忙错误状态
   }
   while(!I2C_xrdy());      //等待数据发送就绪,XRDY=1,表明发送寄存器已经准备好接受新的数据
   I2caRegs.I2CSAR = SlaveAddress&0xff;    //设备从地址
   I2caRegs.I2CCNT = number + 1;   //需要发送的字节数
   I2caRegs.I2CDXR = RomAddress&0xff;    //第一个发送字节为发送数据的目标寄存器地址
   DELAY_US(1);//等待数据完全赋值到I2CDXR,再使能IIC,必须要加上!
   I2caRegs.I2CMDR.all = 0x6E20;    //发送起始信号,内部数据计数器减到0时,发送停止信号,主机发送模式,使能IIC模式,
   for (i=0; i<number; i++)
   {
      while(!I2C_xrdy());   //等待数据发送就绪,发送下一个数据
      I2caRegs.I2CDXR = *Wdata&0xFF;
      Wdata++;
      if (I2caRegs.I2CSTR.bit.NACK == 1)    //送到无应答信号返回错误
          return    I2C_BUS_BUSY_ERROR;
   }
  // DELAY_US(1);
   return I2C_SUCCESS;         //发送成功
}


/**
 *  @brief                  IIC写数据
 *  @parameter              SlaveAddress:从机地址;  RomAddress:寄存器地址; number:写入数据的字节数;  *RamAddr:读出数据存放的地址
 *  @return_value           状态标志
 */
Uint16 ReadData( Uint16 SlaveAddress, Uint16 RomAddress,Uint16 number, Uint16  *RamAddr)
{
   Uint16  i,Temp;

   if (I2caRegs.I2CSTR.bit.BB == 1)  //返回总线忙错误状态
   {
       return I2C_BUS_BUSY_ERROR;
   }
   while(!I2C_xrdy());
   I2caRegs.I2CSAR = SlaveAddress&0xff;      //设备从地址
   I2caRegs.I2CCNT = 1;        //发送一个字节为要读取数据的寄存器地址
   I2caRegs.I2CDXR = RomAddress&0xff;
   DELAY_US(1);//等待数据完全赋值到I2CDXR,再使能IIC,必须要加上!
   I2caRegs.I2CMDR.all = 0x6620;   //主机发送模式,自动清除停止位不产生停止信号
   if (I2caRegs.I2CSTR.bit.NACK == 1)    //送到无应答信号返回错误
        return  I2C_BUS_BUSY_ERROR;
   DELAY_US(50);
   while(!I2C_xrdy());                  //调试过程中有时会卡在这一步,表示通讯不正常
   I2caRegs.I2CSAR = SlaveAddress&0xff;
   I2caRegs.I2CCNT = number;
   DELAY_US(1);//等待数据完全赋值到I2CDXR,再使能IIC,必须要加上!
   I2caRegs.I2CMDR.all = 0x6C20;   //主机接受模式,包含有停止信号
   if (I2caRegs.I2CSTR.bit.NACK == 1)
        return  I2C_BUS_BUSY_ERROR;
   for(i=0;i<number;i++)
   {
         while(!I2C_rrdy());
      Temp = I2caRegs.I2CDRR;
      if (I2caRegs.I2CSTR.bit.NACK == 1)
          return    I2C_BUS_BUSY_ERROR;
      *RamAddr = Temp;
      RamAddr++;
   }
   return I2C_SUCCESS;    //接受数据成功
}

/**
 *  @brief                 IIC向寄存器的某一位写数据
 *  @parameter              SlaveAddress:从机地址;  regaddress:寄存器地址; bitNum:某一位;  data:写入数据
 *  @return_value           无
 */
void IICwriteBit(Uint16 slaveaddress, Uint16 regaddress, Uint8 bitNum, Uint8 data)
{
    Uint16 a;
    Uint8 b;
    DELAY_US(50);
    ReadData(slaveaddress,regaddress,1,&a);
    b=(Uint8 )(a&0xff);
    b = (data != 0) ? (b | (1 << bitNum)) : (b & ~(1 << bitNum));
    DELAY_US(50);
    WriteData(slaveaddress,regaddress,1,&b);
}

/**
 *  @brief                 IIC向寄存器的某几位写数据
 *  @parameter              SlaveAddress:从机地址;  regaddress:寄存器地址; bitStart:开始位;  length:长度;data:写入数据
 *  @return_value           无
 */
void IICwriteBits(Uint16 slaveaddress,Uint16 regaddress,Uint8 bitStart,Uint8 length,Uint8 data)
{

    Uint8 b,mask;
    Uint16 a;
    DELAY_US(50);
    ReadData(slaveaddress,regaddress,1,&a);
    b=(Uint8 )(a&0xff);
    mask = (0xFF << (bitStart + 1)) | 0xFF >> ((8 - bitStart) + length - 1);
    data <<= (8 - length);
    data >>= (7 - bitStart);
    b &= mask;
    b |= data;
    DELAY_US(50);
    WriteData(slaveaddress,regaddress,1, &b);
}

bsp_i2c.h

/**
 *  ********************************************************************************************
 *  @file                 bsp_i2c.h
 *  @file                 SK Electronics
 *  @version           V1.0
 *  @date               2021-xx-xx
 *  @brief               I2C通信函数接口头文件
 *  *******************************************************************************************
 *  @attention
 *  实验平台:SK-F28335Mini   核心板
 *  CSDN博客:https://blog.csdn.net/weixin_46556696
 *  淘宝:https://shop409670932.taobao.com
 */
#include "DSP28x_Project.h"
#ifndef _BSP_I2C_H_
#define _BSP_I2C_H_
#define Uint8 unsigned char
//函数声明
Uint16  ReadData( Uint16 SlaveAddress, Uint16 RomAddress,Uint16 number, Uint16 *RamAddr);
Uint16  WriteData( Uint16 SlaveAddress, Uint16 RomAddress,Uint16 number, Uint8 *Wdata);
void IICwriteBit(Uint16 slaveaddress, Uint16 regaddress, Uint8 bitNum, Uint8 data);
void IICwriteBits(Uint16 slaveaddress,Uint16 regaddress,Uint8 bitStart,Uint8 length,Uint8 data);
Uint16  I2C_xrdy();
Uint16  I2C_rrdy();
void Init_I2CA(void);

#endif /*_BSP_SPI_H_ */

bsp_led.c(RES管脚配置)

/**
 *  ********************************************************************************************
 *  @file                 bsp_led.c
 *  @file                 SK Electronics
 *  @version           V1.0
 *  @date               2020-xx-xx
 *  @brief               LED应用函数接口
 *  *******************************************************************************************
 *  @attention
 *  实验平台:SK-F28335Mini   核心板
 *  CSDN博客:https://blog.csdn.net/weixin_46556696
 *  淘宝:https://shop409670932.taobao.com
 */
#include "bsp_led.h"
void RES_GPIO_Config(void)
{
    EALLOW;
    GpioCtrlRegs.GPAMUX2.bit.GPIO27=0;//普通IO模式
    GpioCtrlRegs.GPAPUD.bit.GPIO27=0;//使能内部上拉
    GpioCtrlRegs.GPADIR.bit.GPIO27=1;//配置成输出
    GpioDataRegs.GPASET.bit.GPIO27=1;//置1
    EDIS;
}

bsp_led.h

/**
 *  ********************************************************************************************
 *  @file                 bsp_led.h
 *  @file                 SK Electronics
 *  @version           V1.0
 *  @date               2020-xx-xx
 *  @brief               LED应用函数接口头文件
 *  *******************************************************************************************
 *  @attention
 *  实验平台:SK-F28335Mini   核心板
 *  CSDN博客:https://blog.csdn.net/weixin_46556696
 *  淘宝:https://shop409670932.taobao.com
 */

#ifndef _BSP_LED_H_
#define _BSP_LED_H_
#include "DSP28x_Project.h"

/* 宏带参,可以像内联函数一样使用,低电平亮灯*/
#define LED0(a) if (a)  \\
                    GpioDataRegs. GPACLEAR.bit.GPIO0=1;\\
                    else        \\
                    GpioDataRegs. GPASET.bit.GPIO0=1

#define LED1(a) if (a)  \\
                    GpioDataRegs. GPACLEAR.bit.GPIO1=1;\\
                    else        \\
                    GpioDataRegs. GPASET.bit.GPIO1=1

#define LED2(a) if (a)  \\
                    GpioDataRegs. GPACLEAR.bit.GPIO2=1;\\
                    else        \\
                    GpioDataRegs. GPASET.bit.GPIO2=1

#define LED3(a) if (a)  \\
                    GpioDataRegs. GPACLEAR.bit.GPIO3=1;\\
                    else        \\
                    GpioDataRegs. GPASET.bit.GPIO3=1

#define LED4(a) if (a)  \\
                    GpioDataRegs. GPACLEAR.bit.GPIO4=1;\\
                    else        \\
                    GpioDataRegs. GPASET.bit.GPIO4=1

/*定义IO口的宏*/
#define LED0_TOGGLE     GpioDataRegs. GPATOGGLE.bit.GPIO0=1
#define LED0_OFF        GpioDataRegs. GPASET.bit.GPIO0=1
#define LED0_ON         GpioDataRegs. GPACLEAR.bit.GPIO0=1

#define LED1_TOGGLE     GpioDataRegs. GPATOGGLE.bit.GPIO1=1
#define LED1_OFF        GpioDataRegs. GPASET.bit.GPIO1=1
#define LED1_ON         GpioDataRegs. GPACLEAR.bit.GPIO1=1

#define LED2_TOGGLE     GpioDataRegs. GPATOGGLE.bit.GPIO2=1
#define LED2_OFF        GpioDataRegs. GPASET.bit.GPIO2=1
#define LED2_ON         GpioDataRegs. GPACLEAR.bit.GPIO2=1

#define LED3_TOGGLE     GpioDataRegs. GPATOGGLE.bit.GPIO3=1
#define LED3_OFF        GpioDataRegs. GPASET.bit.GPIO3=1
#define LED3_ON         GpioDataRegs. GPACLEAR.bit.GPIO3=1

#define LED4_TOGGLE     GpioDataRegs. GPATOGGLE.bit.GPIO4=1
#define LED4_OFF        GpioDataRegs. GPASET.bit.GPIO4=1
#define LED4_ON         GpioDataRegs. GPACLEAR.bit.GPIO4=1

void RES_GPIO_Config(void);
#endif /*_BSP_LED_H_ */

bsp_OLED.c(OLED显示控制)

/**
 *  ********************************************************************************************
 *  @file                 bsp_OLED.c
 *  @file                 SK Electronics
 *  @version           V1.0
 *  @date               2021-xx-xx
 *  @brief               OLED显示函数接口
 *  *******************************************************************************************
 *  @attention
 *  实验平台:SK-F28335Mini   核心板
 *  CSDN博客:https://blog.csdn.net/weixin_46556696
 *  淘宝:https://shop409670932.taobao.com
 */
#include "bsp_OLED.h"
#include "bsp_i2c.h"
#include "oledfont.h"
extern void delay_1ms(Uint16 t);
Uint8 OLED_GRAM[144][8];

/**
 *  @brief                   反显函数
 *  @parameter                  i=0为正常显示;i=1为反色显示
 *  @return_value               无
 */
void OLED_ColorTurn(Uint8 i)
{
    if(i==0)
    {
        OLED_WR_Byte(0xA6,OLED_CMD);//正常显示
    }
    if(i==1)
    {
        OLED_WR_Byte(0xA7,OLED_CMD);//反色显示
    }
}

/**
 *  @brief                   屏幕旋转180度
 *  @parameter                  i=0为正常显示;i=1为反转显示
 *  @return_value               无
 */
void OLED_DisplayTurn(Uint8 i)
{
    if(i==0)
    {
        OLED_WR_Byte(0xC8,OLED_CMD);//正常显示
        OLED_WR_Byte(0xA1,OLED_CMD);
    }
    if(i==1)
    {
        OLED_WR_Byte(0xC0,OLED_CMD);//反转显示
        OLED_WR_Byte(0xA0,OLED_CMD);
    }
}

/**
 *  @brief                   写入一个字节
 *  @parameter                  dat:写入的数据 ; rom:准备写入数据的寄存器地址
 *  @return_value               无
 */
void Send_Byte(Uint8 dat,Uint16 rom)
{
    while(WriteData(devAddr,rom,1,&dat));//等待发送成功
}

/**
 *  @brief                   向OLED发送一个字节
 *  @parameter                  dat: 写入的数据; mode:数据/命令 0表示命令,1表示数据
 *  @return_value               无
 */
void OLED_WR_Byte(Uint8 dat,Uint8 mode)
{
    if(mode)
        Send_Byte(dat,0x40);//写数据
    else
        Send_Byte(dat,0x00);//写命令
}

/**
 *  @brief                   开启显示
 *  @parameter                 无
 *  @return_value               无
 */
void OLED_DisPlay_On(void)
{
    OLED_WR_Byte(0x8D,OLED_CMD);//电荷泵使能
    OLED_WR_Byte(0x14,OLED_CMD);//开启电荷泵
    OLED_WR_Byte(0xAF,OLED_CMD);//开启屏幕
}

/**
 *  @brief                   关闭显示
 *  @parameter                 无
 *  @return_value               无
 */
void OLED_DisPlay_Off(void)
{
    OLED_WR_Byte(0x8D,OLED_CMD);//电荷泵使能
    OLED_WR_Byte(0x10,OLED_CMD);//关闭电荷泵
    OLED_WR_Byte(0xAE,OLED_CMD);//关闭屏幕
}

/**
 *  @brief                   更新显存到OLED
 *  @parameter                 无
 *  @return_value               无
 */
void OLED_Refresh(void)
{
    Uint8 i,n;
    for(i=0;i<8;i++)
    {
        OLED_WR_Byte(0xb0+i,OLED_CMD); //设置行起始地址
        OLED_WR_Byte(0x00,OLED_CMD);   //设置低列起始地址
        OLED_WR_Byte(0x10,OLED_CMD);   //设置高列起始地址
        for(n=0;n<128;n++)
        {
            OLED_WR_Byte(OLED_GRAM[n][i],OLED_DATA);
            //Send_Byte(OLED_GRAM[n][i]);
        }
    }
}

/**
 *  @brief                   清屏函数
 *  @parameter                 无
 *  @return_value               无
 */
void OLED_Clear(void)
{
    Uint8 i,n;
    for(i=0;i<8;i++)
    {
       for(n=0;n<128;n++)
       {
           OLED_GRAM[n][i]=0;//清除所有数据
       }
    }
    OLED_Refresh();//更新显示
}

/**
 *  @brief                   画点
 *  @parameter                 x:0~127; y:0~63; t:1 填充 0,清空
 *  @return_value               无
 */
void OLED_DrawPoint(Uint8 x,Uint8 y,Uint8 t)
{
    Uint8 i,m,n;
    i=y/8;
    m=y%8;
    n=1<<m;
    if(t){OLED_GRAM[x][i]|=n;}
    else
    {
        OLED_GRAM[x][i]=~OLED_GRAM[x][i];
        OLED_GRAM[x][i]|=n;
        OLED_GRAM[x][i]=~OLED_GRAM[x][i];
    }
}

/**
 *  @brief                   画线
 *  @parameter              x1,y1:起始坐标;  x2,y2:结束坐标
 *  @return_value           无
 */
void OLED_DrawLine(Uint8 x1,Uint8 y1,Uint8 x2,Uint8 y2,Uint8 mode)
{
    Uint16 t;
    int xerr=0,yerr=0,delta_x,delta_y,distance;
    int incx,incy,uRow,uCol;
    delta_x=x2-x1; //计算坐标增量
    delta_y=y2-y1;
    uRow=x1;//画线起点
    uCol=y1;
    if(delta_x>0)incx=1; //设置单步方向
    else if (delta_x==0)incx=0;//垂直线
    else {incx=-1;delta_x=

以上是关于DSP28335基础教程——I2C通信实验(OLED显示控制)的主要内容,如果未能解决你的问题,请参考以下文章

DSP28335基础教程——I2C通信实验(OLED显示控制)

DSP28335基础教程——I2C通信实验(OLED显示控制)

DSP28335基础教程——SCI串口通信实验(上位机收发显示)

DSP28335基础教程——SCI串口通信实验(上位机收发显示)

DSP28335基础教程——SPI通信实验(TFT显示控制)

DSP28335基础教程——SPI通信实验(TFT显示控制)