瑞萨单片机论坛
直播中

记帖MCU

2年用户 137经验值
擅长:嵌入式技术 控制/MCU RF/无线
私信 关注

【RA4M2设计挑战赛】基于IIC驱动OLED

概述

  1. 在e2studio中创建新的工程并导入必要的文件,包括I2C驱动代码和SSD1306 OLED显示驱动代码。
  2. 配置RA4M2的I2C接口,使其作为I2C master进行通信。
  3. 初始化SSD1306 OLED显示驱动代码,并配置显示屏的物理地址和分辨率。
  4. 通过I2C驱动代码将数据写入SSD1306 OLED显示屏。
  5. 在e2studio中运行代码并检查显示屏是否正常显示数据。

注意:具体的代码细节可能因项目而异,请确保遵循厂商的数据手册和应用程序示例。

硬件准备

首先需要准备一个开发板,这里我准备的是芯片型号R7FAM2AD3CFP的开发板:

新建工程

工程模板

保存工程路径

芯片配置

本文中使用R7FA4M2AD3CFP来进行演示。

工程模板选择

时钟设置

开发板上的外部高速晶振为12M.

需要修改XTAL为12M。

IIC属性配置

查看手册,可以得知OLED的IIC地址为“b0111100” or “b0111101”,即0x3c或者0x3d。

在OLED的模块中,丝印上面写的地址是0x78(b0111 1000)或者0x7a(b0111 1010),是因为加了一位RW位,因为一般只要发送数据让屏幕进行显示。

IIC配置

配置RA4M2的I2C接口,使其作为I2C master进行通信。

查看开发板原理图,对应的IIC为P408和P409。

点击Stacks->New Stack->Connectivity -> I2C Master(r_sci_i2c)。

设置IIC的配置,需要注意从机的地址,在stm32等程序中写的是0x78,但是这里的地址位数是7位,故需要右移一位,变成0x3c。

R_SCI_I2C_Open()函数原型

R_SCI_I2C_Open()函数为执行IIC初始化,开启配置如下所示。

/* Initialize the I2C module */
       err = R_SCI_I2C_Open(&g_i2c3_ctrl, &g_i2c3_cfg);
       /* Handle any errors. This function should be defined by the user. */
       assert(FSP_SUCCESS == err);

R_SCI_I2C_Write()函数原型

R_SCI_I2C_Write()函数是向IIC设备中写入数据,写入格式如下所示。

err = R_SCI_I2C_Write(&g_i2c_device_ctrl_1, &g_i2c_tx_buffer[0], I2C_BUFFER_SIZE_BYTES, false);
    assert(FSP_SUCCESS == err);

sci_i2c_master_callback()回调函数

对于数据是否发送完毕,可以查看是否获取到I2C_MASTER_EVENT_TX_COMPLETE字段。

/* Callback function */
i2c_master_event_t i2c_event = I2C_MASTER_EVENT_ABORTED;
void sci_i2c_master_callback(i2c_master_callback_args_t *p_args)
{
    i2c_event = I2C_MASTER_EVENT_ABORTED;
    if (NULL != p_args)
    {
        /* capture callback event for validating the i2c transfer event*/
        i2c_event = p_args->event;
    }
}

控制与发送数据

对于发送数据,可以使用R_SCI_I2C_Write()函数,对于是否发送完毕,可以判断I2C_MASTER_EVENT_TX_COMPLETE != i2c_event,并且引入超时机制。

向0x3c设备写命令0x00的IIC_Command指令。

/**********************************************
 IIC Write Command
**********************************************/
void Write_IIC_Command(unsigned char IIC_Command)
{

    uint8_t ii[2]={0x00,0x00};
    ii[1] = IIC_Command;



    err = R_SCI_I2C_Write(&g_i2c3_ctrl, ii, 0x02, true);
           assert(FSP_SUCCESS == err);
           /* Since there is nothing else to do, block until Callback triggers*/
           //while ((I2C_MASTER_EVENT_TX_COMPLETE != i2c_event) && timeout_ms)
           while ((I2C_MASTER_EVENT_TX_COMPLETE != i2c_event) && timeout_ms>0)
           {
               R_BSP_SoftwareDelay(100U, BSP_DELAY_UNITS_MICROSECONDS);
               timeout_ms--;
           }
           if (I2C_MASTER_EVENT_ABORTED == i2c_event)
           {
               __BKPT(0);
           }
           /* Read data back from the I2C slave */
           i2c_event = I2C_MASTER_EVENT_ABORTED;
           timeout_ms           = 100000;
}

IIC写数据

向0x3c设备写数据0x40的IIC_Data指令。

/**********************************************
// IIC Write Data
**********************************************/
void Write_IIC_Data(unsigned char IIC_Data)
{


    uint8_t ii[2]={0x40,0x00};
    ii[0] = 0x40;
    ii[1] = IIC_Data;
    err = R_SCI_I2C_Write(&g_i2c3_ctrl, ii, 0x02, true);
           assert(FSP_SUCCESS == err);
           /* Since there is nothing else to do, block until Callback triggers*/
           //while ((I2C_MASTER_EVENT_TX_COMPLETE != i2c_event) && timeout_ms)
           while ((I2C_MASTER_EVENT_TX_COMPLETE != i2c_event) && timeout_ms>0)
           {
               R_BSP_SoftwareDelay(100U, BSP_DELAY_UNITS_MICROSECONDS);
               timeout_ms--;
           }
           if (I2C_MASTER_EVENT_ABORTED == i2c_event)
           {
               __BKPT(0);
           }
           /* Read data back from the I2C slave */
           i2c_event = I2C_MASTER_EVENT_ABORTED;
           timeout_ms           = 100000;

}

驱动代码

若需要让OLED显示字符或者图片,需要添加驱动文件,添加方式如下。

主要需要添加如下四个文件。

oled.c:该文件包含控制OLED屏幕并与之通信的主要代码。具有初始化屏幕、绘制图形和文本以及更新显示的函数。

oled.h:这个头文件可以声明函数原型和任何在oled.c文件中使用的常量或变量。

bmp.h:该文件包含在OLED屏幕上呈现BMP图像文件的代码。

oledfont.h:这个文件包含在OLED屏幕上渲染自定义字体的代码,或者可能声明一个用于显示文本的字体表。

这四个文件附录在最后。

字符取模

由于单片机的flash比较吃紧,所以要显示汉字需要先进行取模。首先需要设置为字符模式。

取模软件设置。

设置完之后就可以取自己想要汉字的编码,输入完毕点击生成字模即可。

在oledfont.h中放入字库。

字体大小只有2个。

在hal_entry.c中添加显示

/* Initialize the I2C module */
    err = R_SCI_I2C_Open(&g_i2c3_ctrl, &g_i2c3_cfg);
    /* Handle any errors. This function should be defined by the user. */
    assert(FSP_SUCCESS == err);

    OLED_Init();            //初始化OLED
    OLED_Clear()    ;
    OLED_ShowCHinese(0,0,0);//记
    OLED_ShowCHinese(16,0,1);//帖

    OLED_ShowNum(0,2,2023,4,16);//显示ASCII字符的码值
    OLED_ShowCHinese(32,2,2);//中文字->年
    OLED_ShowNum(48,2,1,2,16);//显示ASCII字符的码值
    OLED_ShowCHinese(64,2,3);//中文字->月
    OLED_ShowNum(80,2,31,2,16);//显示ASCII字符的码值
    OLED_ShowCHinese(96,2,4);//中文字->日
    OLED_ShowString(0,4,"ZXY",12);
    OLED_ShowString(0,6,"RA4M2",16);

显示结果如下所示。

图片取模

设置为图形模式。

格式设置

选择需要打开的文件。

打开的文件必须为bmp文件。

选择生成字模。

图片数据放在bmp.h中。

显示图片

/* Initialize the I2C module */
    err = R_SCI_I2C_Open(&g_i2c3_ctrl, &g_i2c3_cfg);
    /* Handle any errors. This function should be defined by the user. */
    assert(FSP_SUCCESS == err);

    OLED_Init();            //初始化OLED
    OLED_Clear()    ;
    OLED_ShowCHinese(0,0,0);//记
    OLED_ShowCHinese(16,0,1);//帖

    OLED_ShowNum(0,2,2023,4,16);//显示ASCII字符的码值
    OLED_ShowCHinese(32,2,2);//中文字->年
    OLED_ShowNum(48,2,1,2,16);//显示ASCII字符的码值
    OLED_ShowCHinese(64,2,3);//中文字->月
    OLED_ShowNum(80,2,31,2,16);//显示ASCII字符的码值
    OLED_ShowCHinese(96,2,4);//中文字->日
    OLED_ShowString(0,4,"ZXY",12);
    OLED_ShowString(0,6,"RA4M2",16);

    /* Wait for minimum time required between transfers. */
    R_BSP_SoftwareDelay(2, BSP_DELAY_UNITS_SECONDS);
    OLED_Clear();
    OLED_DrawBMP(0,0,64,8,BMP1); //图片显示(图片显示慎用,生成的字表较大,会占用较多空间,FLASH空间8K以下慎用)

主程序

#include "hal_data.h"
#include "oled.h"
#include "bmp.h"
FSP_CPP_HEADER
void R_BSP_WarmStart(bsp_warm_start_event_t event);
FSP_CPP_FOOTER


/* Callback function */
i2c_master_event_t i2c_event = I2C_MASTER_EVENT_ABORTED;
void sci_i2c_master_callback(i2c_master_callback_args_t *p_args)
{
    i2c_event = I2C_MASTER_EVENT_ABORTED;
    if (NULL != p_args)
    {
        /* capture callback event for validating the i2c transfer event*/
        i2c_event = p_args->event;
    }
}

fsp_err_t err = FSP_SUCCESS;
uint32_t  timeout_ms = 100000;

/*******************************************************************************************************************//**
 * main() is generated by the RA Configuration editor and is used to generate threads if an RTOS is used.  This function
 * is called by main() when no RTOS is used.
 **********************************************************************************************************************/
void hal_entry(void)
{
    /* TODO: add your own code here */


    /* Initialize the I2C module */
    err = R_SCI_I2C_Open(&g_i2c3_ctrl, &g_i2c3_cfg);
    /* Handle any errors. This function should be defined by the user. */
    assert(FSP_SUCCESS == err);

    OLED_Init();            //初始化OLED
    OLED_Clear()    ;
    OLED_ShowCHinese(0,0,0);//记
    OLED_ShowCHinese(16,0,1);//帖

    OLED_ShowNum(0,2,2023,4,16);//显示ASCII字符的码值
    OLED_ShowCHinese(32,2,2);//中文字->年
    OLED_ShowNum(48,2,1,2,16);//显示ASCII字符的码值
    OLED_ShowCHinese(64,2,3);//中文字->月
    OLED_ShowNum(80,2,31,2,16);//显示ASCII字符的码值
    OLED_ShowCHinese(96,2,4);//中文字->日
    OLED_ShowString(0,4,"ZXY",12);
    OLED_ShowString(0,6,"RA4M2",16);

    /* Wait for minimum time required between transfers. */
    R_BSP_SoftwareDelay(2, BSP_DELAY_UNITS_SECONDS);
    OLED_Clear();
    OLED_DrawBMP(0,0,64,8,BMP1); //图片显示(图片显示慎用,生成的字表较大,会占用较多空间,FLASH空间8K以下慎用)



#if BSP_TZ_SECURE_BUILD
    /* Enter non-secure code */
    R_BSP_NonSecureEnter();
#endif
}

oled.c

/*
 * oled.c
 *
 *  Created on: 2023年1月31日
 *      Author: a8456
 */


//
//本程序只供学习使用,未经作者许可,不得用于其它任何用途
//记帖Q群:615061293
//B站视频:https://space.bilibili.com/26152390
//CSDN:https://blog.csdn.net/qq_24312945/
//  文 件 名   : main.c
//  版 本 号   : v2.0
//  作    者   : Evk123
//  生成日期   : 2014-0101
//  最近修改   :
//  功能描述   : 0.69寸OLED 接口演示例程(STM32F103ZE系列IIC)
//              说明:
//              ----------------------------------------------------------------
//              GND   电源地
//              VCC   接5V或3.3v电源
//              SCL   接PA5(SCL)
//              SDA   接PA7(SDA)
//              ----------------------------------------------------------------
//记帖Q群:615061293
//All rights reserved
//
#include "oled.h"
#include "stdlib.h"
#include "oledfont.h"

#include "hal_data.h"
extern fsp_err_t err;
extern int  timeout_ms;
extern  i2c_master_event_t i2c_event ;
//OLED的显存
//存放格式如下.
//[0]0 1 2 3 ... 127
//[1]0 1 2 3 ... 127
//[2]0 1 2 3 ... 127
//[3]0 1 2 3 ... 127
//[4]0 1 2 3 ... 127
//[5]0 1 2 3 ... 127
//[6]0 1 2 3 ... 127
//[7]0 1 2 3 ... 127

/**********************************************
 IIC Write Command
**********************************************/
void Write_IIC_Command(unsigned char IIC_Command)
{

    uint8_t ii[2]={0x00,0x00};
    ii[1] = IIC_Command;



    err = R_SCI_I2C_Write(&g_i2c3_ctrl, ii, 0x02, true);
           assert(FSP_SUCCESS == err);
           /* Since there is nothing else to do, block until Callback triggers*/
           //while ((I2C_MASTER_EVENT_TX_COMPLETE != i2c_event) && timeout_ms)
           while ((I2C_MASTER_EVENT_TX_COMPLETE != i2c_event) && timeout_ms>0)
           {
               R_BSP_SoftwareDelay(100U, BSP_DELAY_UNITS_MICROSECONDS);
               timeout_ms--;
           }
           if (I2C_MASTER_EVENT_ABORTED == i2c_event)
           {
               __BKPT(0);
           }
           /* Read data back from the I2C slave */
           i2c_event = I2C_MASTER_EVENT_ABORTED;
           timeout_ms           = 100000;
}

/**********************************************
// IIC Write Data
**********************************************/
void Write_IIC_Data(unsigned char IIC_Data)
{


    uint8_t ii[2]={0x40,0x00};
    ii[0] = 0x40;
    ii[1] = IIC_Data;
    err = R_SCI_I2C_Write(&g_i2c3_ctrl, ii, 0x02, true);
           assert(FSP_SUCCESS == err);
           /* Since there is nothing else to do, block until Callback triggers*/
           //while ((I2C_MASTER_EVENT_TX_COMPLETE != i2c_event) && timeout_ms)
           while ((I2C_MASTER_EVENT_TX_COMPLETE != i2c_event) && timeout_ms>0)
           {
               R_BSP_SoftwareDelay(100U, BSP_DELAY_UNITS_MICROSECONDS);
               timeout_ms--;
           }
           if (I2C_MASTER_EVENT_ABORTED == i2c_event)
           {
               __BKPT(0);
           }
           /* Read data back from the I2C slave */
           i2c_event = I2C_MASTER_EVENT_ABORTED;
           timeout_ms           = 100000;

}
void OLED_WR_Byte(unsigned dat,unsigned cmd)
{
    if(cmd)
            {

   Write_IIC_Data(dat);

        }
    else {
   Write_IIC_Command(dat);

    }


}


/********************************************
// fill_Picture
********************************************/
void fill_picture(unsigned char fill_Data)
{
    unsigned char m,n;
    for(m=0;m<8;m++)
    {
        OLED_WR_Byte(0xb0+m,0);     //page0-page1
        OLED_WR_Byte(0x00,0);       //low column start address
        OLED_WR_Byte(0x10,0);       //high column start address
        for(n=0;n<128;n++)
            {
                OLED_WR_Byte(fill_Data,1);
            }
    }
}


/***********************Delay****************************************/
void Delay_50ms(unsigned int Del_50ms)
{
    unsigned int m;
    for(;Del_50ms>0;Del_50ms--)
        for(m=6245;m>0;m--);
}

void Delay_1ms(unsigned int Del_1ms)
{
    unsigned char j;
    while(Del_1ms--)
    {
        for(j=0;j<123;j++);
    }
}

//坐标设置

    void OLED_Set_Pos(unsigned char x, unsigned char y)
{   OLED_WR_Byte(0xb0+y,OLED_CMD);
    OLED_WR_Byte(((x&0xf0)>>4)|0x10,OLED_CMD);
    OLED_WR_Byte((x&0x0f),OLED_CMD);
}
//开启OLED显示
void OLED_Display_On(void)
{
    OLED_WR_Byte(0X8D,OLED_CMD);  //SET DCDC命令
    OLED_WR_Byte(0X14,OLED_CMD);  //DCDC ON
    OLED_WR_Byte(0XAF,OLED_CMD);  //DISPLAY ON
}
//关闭OLED显示
void OLED_Display_Off(void)
{
    OLED_WR_Byte(0X8D,OLED_CMD);  //SET DCDC命令
    OLED_WR_Byte(0X10,OLED_CMD);  //DCDC OFF
    OLED_WR_Byte(0XAE,OLED_CMD);  //DISPLAY OFF
}
//清屏函数,清完屏,整个屏幕是黑色的!和没点亮一样!!!
void OLED_Clear(void)
{
    u8 i,n;
    for(i=0;i<8;i++)
    {
        OLED_WR_Byte (0xb0+i,OLED_CMD);    //设置页地址(0~7)
        OLED_WR_Byte (0x00,OLED_CMD);      //设置显示位置—列低地址
        OLED_WR_Byte (0x10,OLED_CMD);      //设置显示位置—列高地址
        for(n=0;n<128;n++)OLED_WR_Byte(0,OLED_DATA);
    } //更新显示
}
void OLED_On(void)
{
    u8 i,n;
    for(i=0;i<8;i++)
    {
        OLED_WR_Byte (0xb0+i,OLED_CMD);    //设置页地址(0~7)
        OLED_WR_Byte (0x00,OLED_CMD);      //设置显示位置—列低地址
        OLED_WR_Byte (0x10,OLED_CMD);      //设置显示位置—列高地址
        for(n=0;n<128;n++)OLED_WR_Byte(1,OLED_DATA);
    } //更新显示
}
//在指定位置显示一个字符,包括部分字符
//x:0~127
//y:0~63
//mode:0,反白显示;1,正常显示
//size:选择字体 16/12
void OLED_ShowChar(u8 x,u8 y,u8 chr,u8 Char_Size)
{
    unsigned char c=0,i=0;
        c=chr-' ';//得到偏移后的值
        if(x>Max_Column-1){x=0;y=y+2;}
        if(Char_Size ==16)
            {
            OLED_Set_Pos(x,y);
            for(i=0;i<8;i++)
            OLED_WR_Byte(F8X16[c*16+i],OLED_DATA);
            OLED_Set_Pos(x,y+1);
            for(i=0;i<8;i++)
            OLED_WR_Byte(F8X16[c*16+i+8],OLED_DATA);
            }
            else {
                OLED_Set_Pos(x,y);
                for(i=0;i<6;i++)
                OLED_WR_Byte(F6x8[c][i],OLED_DATA);

            }
}
//m^n函数
u32 oled_pow(u8 m,u8 n)
{
    u32 result=1;
    while(n--)result*=m;
    return result;
}
//显示2个数字
//x,y :起点坐标
//len :数字的位数
//size:字体大小
//mode:模式   0,填充模式;1,叠加模式
//num:数值(0~4294967295);
void OLED_ShowNum(u8 x,u8 y,u32 num,u8 len,u8 size2)
{
    u8 t,temp;
    u8 enshow=0;
    for(t=0;t<len;t++)
    {
        temp=(num/oled_pow(10,len-t-1))%10;
        if(enshow==0&&t<(len-1))
        {
            if(temp==0)
            {
                OLED_ShowChar(x+(size2/2)*t,y,' ',size2);
                continue;
            }else enshow=1;

        }
        OLED_ShowChar(x+(size2/2)*t,y,temp+'0',size2);
    }
}
//显示一个字符号串
void OLED_ShowString(u8 x,u8 y,u8 *chr,u8 Char_Size)
{
    unsigned char j=0;
    while (chr[j]!='\0')
    {       OLED_ShowChar(x,y,chr[j],Char_Size);
            x+=8;
        if(x>120){x=0;y+=2;}
            j++;
    }
}
//显示汉字
void OLED_ShowCHinese(u8 x,u8 y,u8 no)
{
    u8 t,adder=0;
    OLED_Set_Pos(x,y);
    for(t=0;t<16;t++)
        {
                OLED_WR_Byte(Hzk[2*no][t],OLED_DATA);
                adder+=1;
     }
        OLED_Set_Pos(x,y+1);
    for(t=0;t<16;t++)
            {
                OLED_WR_Byte(Hzk[2*no+1][t],OLED_DATA);
                adder+=1;
      }
}
/***********功能描述:显示显示BMP图片128×64起始点坐标(x,y),x的范围0~127,y为页的范围0~7*****************/
void OLED_DrawBMP(unsigned char x0, unsigned char y0,unsigned char x1, unsigned char y1,unsigned char BMP[])
{
 unsigned int j=0;
 unsigned char x,y;

  if(y1%8==0) y=y1/8;
  else y=y1/8+1;
    for(y=y0;y<y1;y++)
    {
        OLED_Set_Pos(x0,y);
    for(x=x0;x<x1;x++)
        {
            OLED_WR_Byte(BMP[j++],OLED_DATA);
        }
    }
}

//初始化SSD1306
void OLED_Init(void)
{

//    GPIO_InitTypeDef  GPIO_InitStructure;
//
//
//    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);    //使能A端口时钟
//    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5|GPIO_Pin_7;
//    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;         //推挽输出
//    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//速度50MHz
//    GPIO_Init(GPIOA, &GPIO_InitStructure);    //初始化GPIOD3,6
//    GPIO_SetBits(GPIOA,GPIO_Pin_5|GPIO_Pin_7);


//delay_ms(800);


    /* Wait for minimum time required between transfers. */
    R_BSP_SoftwareDelay(800, BSP_DELAY_UNITS_MICROSECONDS);

OLED_WR_Byte(0xAE,OLED_CMD);//--display off
    OLED_WR_Byte(0x00,OLED_CMD);//---set low column address
    OLED_WR_Byte(0x10,OLED_CMD);//---set high column address
    OLED_WR_Byte(0x40,OLED_CMD);//--set start line address
    OLED_WR_Byte(0xB0,OLED_CMD);//--set page address
    OLED_WR_Byte(0x81,OLED_CMD); // contract control
    OLED_WR_Byte(0xFF,OLED_CMD);//--128
    OLED_WR_Byte(0xA1,OLED_CMD);//set segment remap
    OLED_WR_Byte(0xA6,OLED_CMD);//--normal / reverse
    OLED_WR_Byte(0xA8,OLED_CMD);//--set multiplex ratio(1 to 64)
    OLED_WR_Byte(0x3F,OLED_CMD);//--1/32 duty
    OLED_WR_Byte(0xC8,OLED_CMD);//Com scan direction
    OLED_WR_Byte(0xD3,OLED_CMD);//-set display offset
    OLED_WR_Byte(0x00,OLED_CMD);//

    OLED_WR_Byte(0xD5,OLED_CMD);//set osc division
    OLED_WR_Byte(0x80,OLED_CMD);//

    OLED_WR_Byte(0xD8,OLED_CMD);//set area color mode off
    OLED_WR_Byte(0x05,OLED_CMD);//

    OLED_WR_Byte(0xD9,OLED_CMD);//Set Pre-Charge Period
    OLED_WR_Byte(0xF1,OLED_CMD);//

    OLED_WR_Byte(0xDA,OLED_CMD);//set com pin configuartion
    OLED_WR_Byte(0x12,OLED_CMD);//

    OLED_WR_Byte(0xDB,OLED_CMD);//set Vcomh
    OLED_WR_Byte(0x30,OLED_CMD);//

    OLED_WR_Byte(0x8D,OLED_CMD);//set charge pump enable
    OLED_WR_Byte(0x14,OLED_CMD);//

    OLED_WR_Byte(0xAF,OLED_CMD);//--turn on oled panel
}

*附件:RA4M2_IIC_OLED.zip

*附件:0.96寸OLED使用文档新手必看V2.0.pdf

*附件:OLE显示屏专用取模工具_PCtoLCD2002.rar

*附件:0.96OLED显示屏数据手册.zip

回帖(1)

记帖MCU

2023-1-31 17:13:56
【RA4M2设计挑战赛】基于IIC驱动OLED,可以下载
举报
附件: 您需要登录才可以下载或查看附件。没有帐号?注册

更多回帖

发帖
×
20
完善资料,
赚取积分