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所示。
由于该OLED模块默认是4线SPI的,为了换成支持I2C方式,从图中我们把电阻R1、R4、R6、R7焊上4.7k的电阻,R3电阻去掉,R8电阻用0欧短接起来即可。
表1给出OLED模块的管脚定义,其中OLED在I2C通信方式中,管脚DC不再是用于数据和命令选择,它是用来作于从机地址。在SSD1306的数据手册中,图2表示了它的7位从机地址与SA0有关,而SA0又是DC管脚的电平状态,在这里我们DC接地,则7位从机地址为0x3C。
注意:在硬件I2C里,R/W#是会自动发送的,我们不能把这一位当做从机地址的一部分,不清楚的朋友可能会认为从机地址是0x78(R/W#=0),但这是错的。因此我们在看数据手册应要注意给出的地址有没有包括R/W#!
不懂I2C的具体工作原理的,百度搜一下非常多,这里不在赘述。接下来是与DSP28335的连线,图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串口通信实验(上位机收发显示)