怀揣少年梦 发表于 2022-5-19 18:15

RTT&瑞萨高性能 CPK-RA6M4 开发板测评——SPI驱动OLED

<h1 id="rttandamp瑞萨高性能-cpk-ra6m4-开发板测评spi驱动oled">一、硬件</h1>

<h3 id="1、oled引脚介绍">1、OLED引脚介绍</h3>

<p>OLED引脚,是7针SPI模式。</p>

<p>1). GND&nbsp;电源地</p>

<p>2). VCC&nbsp;电源正(3~5.5V)</p>

<p>3). D0 OLED&nbsp;的&nbsp;D0&nbsp;脚,在&nbsp;SPI&nbsp;和&nbsp;IIC&nbsp;通信中为时钟管脚</p>

<p>4). D1 OLED&nbsp;的&nbsp;D1&nbsp;脚,在&nbsp;SPI&nbsp;和&nbsp;IIC&nbsp;通信中为数据管脚</p>

<p>5). RES OLED&nbsp;的&nbsp;RES#脚,用来复位(低电平复位)</p>

<p>6). DC OLED&nbsp;的&nbsp;D/C#E&nbsp;脚,数据和命令控制管脚</p>

<p>如图所示:实物引脚。</p>

<p></p>

<p>关于OLED的显示原理就不介绍。</p>

<h3 id="2、接线">2、接线</h3>

<p>1)、VCC接板载3.3V引脚,与板子共地</p>

<p>2)、把SPI0的相应时钟SCLK、数据MOSI关机与OLED的时钟和数据管脚,使用杜邦线连接起来,再使用其他引脚,作为普通管脚,控制OLED的片选CS、复位RES、DC引脚。</p>

<h2 id="二、软件操作">二、软件操作</h2>

<h3 id="1、ra-smart-configurator操作">1、RA Smart Configurator操作</h3>

<p>1)、使用瑞萨灵活配置软件添加SPI0的复用引脚相关的时钟与MOSI引脚,注意,程序中是软件配置片选,因此SPI0硬件片选引脚CS 配置为普通引脚(P703)。同时设置P406(对应OLED的DC)、P405(对应OLED的RES)为普通输出引脚。</p>

<p></p>

<p>2)、设置三个普通引脚模式为输出模式,并且设置为初始化为高电平</p>

<p></p>

<p>3)、添加SPI的配置,设置SPI0的回调函数为spi0_callback。其他都默认,可以在对应的参数设置中,设置SPI工作模式、速率、相位、极性等。另外也可以直接在RTThread中修改相应的配置。</p>

<p></p>

<h3 id="2、rtthread-studio操作">2、RTThread-Studio操作</h3>

<p>1)、添加RTThread对RA6M4支持的SPI驱动。</p>

<p></p>

<p>2)、使能SPI0驱动。</p>

<p></p>

<h3 id="3、代码">3、代码</h3>

<p>1)、OLED驱动代码</p>

<pre>
<code>/*
* Copyright (c) 2006-2021, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date         Author       Notes
* 2022-05-01   LD       the first version
*/
#include &quot;OLED.h&quot;
#include &quot;oledfont.h&quot;
#include &lt;rtthread.h&gt;
#include &lt;stdio.h&gt;
#include &lt;drv_spi.h&gt;
//static struct rt_spi_device spi_dev_lcd;

/*OLED段码分布
正常
(page0)      SEG0-SEG128
                  COM0
                     |
                  COM7

反置
(page0)       SEG128-SEG0
                  COM63
                     |
                  COM56

写命令,其实是写寄存器
*/
static struct rt_spi_device spi_dev_oled;

static int rt_hw_oled_config(void)
{
    char user_data[] = {1};
    if (rt_spi_bus_attach_device(&amp;spi_dev_oled, &quot;oled&quot;, &quot;spi0&quot;, user_data) != RT_EOK)
      return -RT_ERROR;

    /* config spi */
    {
      struct rt_spi_configuration cfg;
      cfg.data_width = 8;
      cfg.mode = RT_SPI_MASTER | RT_SPI_MODE_0 | RT_SPI_MSB;
      cfg.max_hz = 5 * 1000 * 1000; /* 42M,SPI max 42MHz,lcd 4-wire spi */

      rt_spi_configure(&amp;spi_dev_oled, &amp;cfg);
    }

    rt_pin_mode(OLED_DC, PIN_MODE_OUTPUT);
    rt_pin_mode(OLED_RES, PIN_MODE_OUTPUT);

    return RT_EOK;
}


/****************************************************************************
* 名 称:void OLED_Init(void)
* 功 能:OLED初始化
* 入口参数:无
* 出口参数:无
* 说 明:参照上电序列初始化(24/40)
****************************************************************************/
int OLED_Init(void)
{
    rt_hw_oled_config();

    OLED_CS_High;
    //复位OLED
    OLED_RST_High;
    rt_thread_mdelay(200);
    OLED_RST_Low;
    rt_thread_mdelay(200);
    OLED_RST_High;

    OLED_Write_Operate(OLED_Mode_Cmd,0xAE); //关闭OLED
    OLED_Write_Operate(OLED_Mode_Cmd,0x00); //设置列低位地址
    OLED_Write_Operate(OLED_Mode_Cmd,0x10); //设置列高位地址
    OLED_Write_Operate(OLED_Mode_Cmd,0x40); //设置起始行地址及映射RAM显示起始行 (0x00~0x3F)
    OLED_Write_Operate(OLED_Mode_Cmd,0xB0); //设置页地址
    OLED_Write_Operate(OLED_Mode_Cmd,0x81); //对比度设置
    OLED_Write_Operate(OLED_Mode_Cmd,0xCF); //Set SEG Output Current Brightness

    OLED_Write_Operate(OLED_Mode_Cmd,0xA1); //Set SEG/Column Mapping   0xa0左右反置 0xa1正常
    OLED_Write_Operate(OLED_Mode_Cmd,0xA6); //
    OLED_Write_Operate(OLED_Mode_Cmd,0xA8); //设置驱动路数(1 to 64)
    OLED_Write_Operate(OLED_Mode_Cmd,0x3F); //*duty = 1/64*/
    OLED_Write_Operate(OLED_Mode_Cmd,0xC8); //0xc0上下反置 0xc8正常
    OLED_Write_Operate(OLED_Mode_Cmd,0xD3); //设置显示偏移(0x00~0x3F)
    OLED_Write_Operate(OLED_Mode_Cmd,0x00); //not offset
    OLED_Write_Operate(OLED_Mode_Cmd,0xD5); //--set Display clock divide ratio/oscillator frequency
    OLED_Write_Operate(OLED_Mode_Cmd,0x80); //--set divide ratio, Set Clock as 100 Frames/Sec
    OLED_Write_Operate(OLED_Mode_Cmd,0xD9); //*set pre-charge period*/
    OLED_Write_Operate(OLED_Mode_Cmd,0Xf1); //Set Pre-Charge as 15 Clocks &amp; Discharge as 1 Clock
    OLED_Write_Operate(OLED_Mode_Cmd,0xDA); //*set COM pins*/
    OLED_Write_Operate(OLED_Mode_Cmd,0x12); //
    OLED_Write_Operate(OLED_Mode_Cmd,0xdb); //*set vcomh*/
    OLED_Write_Operate(OLED_Mode_Cmd,0x40); //Set VCOM Deselect Level
    /**/
    OLED_Write_Operate(OLED_Mode_Cmd,0x20); //设置存储模式(0x00,0x02,0x01)
    OLED_Write_Operate(OLED_Mode_Cmd,0x02); //页地址模式

    OLED_Write_Operate(OLED_Mode_Cmd,0x8d); //*set charge pump disable*/
    OLED_Write_Operate(OLED_Mode_Cmd,0x14); //--set(0x10) disable
    /**/
    OLED_Write_Operate(OLED_Mode_Cmd,0xA4); //显示开启(A4,显示,A5无显示)
    OLED_Write_Operate(OLED_Mode_Cmd,0xA6); //背景正反向显示(0xa6,正显,0xa7,反显)


    OLED_Display_Clear();                                       //清屏
    OLED_Write_Operate(OLED_Mode_Cmd,0xAF); //打开显示

    return 0;
}
INIT_COMPONENT_EXPORT(OLED_Init);

/****************************************************************************
* 名 称:u8 SPI1_WriteReadByte(u8 Data)
* 功 能:SPI1读写一个字节
* 入口参数:无
* 出口参数:接收的数据
* 返回值:0,发送接收超时,1成功
* 说 明:无
****************************************************************************/
uint8_t g_master_rx_buff = {0};
static rt_err_t SPI0_WriteReadByte(const rt_uint8_t Data)
{
    rt_size_t len = 0;

    rt_spi_transfer(&amp;spi_dev_oled, (const void *)&amp;Data, NULL, 1);
    if (len != 1)
    {
      LOG_I(&quot;ssd1306_write_data error. %d&quot;, len);
      return -RT_ERROR;
    }
    else
    {
      return RT_EOK;
    }
    return RT_EOK;
}

/****************************************************************************
* 名 称:u8 OLED_Write_Byte(u8 data)
* 功 能:OLED写一个字节
* 入口参数:无
* 出口参数:接收的数据
* 返回值:0失败,1成功
* 说 明:无
****************************************************************************/
u8 OLED_Write_Byte(u8 data)
{
    if(SPI0_WriteReadByte(data))
      return 0;
    else
      return 1;
}


/****************************************************************************
* 名 称:void OLED_Write_Operate(u8 mode,u8 data)
* 功 能:OLED写操作
* 入口参数:模式,数据
* 出口参数:无
* 返回值:无
* 说 明:无
****************************************************************************/
void OLED_Write_Operate(u8 mode,u8 data)
{
    OLED_CS_Low;

    if(mode)
    {
      OLED_Write_Data;
    }
    else
    {
      OLED_Write_Cmd;
    }

    OLED_Write_Byte(data);

    OLED_CS_High;
}


/****************************************************************************
* 名 称:void OLED_Display_Clear(void)
* 功 能:OLED清屏
* 入口参数:无
* 出口参数:无
* 返回值:无
* 说 明:无
****************************************************************************/
void OLED_Display_Clear(void)
{
    u8 page = 0;
    u8 i=0;
    OLED_CS_Low;

    for(page=0;page &lt; 8; page++)
    {
      OLED_Write_Operate(OLED_Mode_Cmd,0xB0+page);//设置页地址
      OLED_Write_Operate(OLED_Mode_Cmd,0x00);   //设置显示位置-列低地址
      OLED_Write_Operate(OLED_Mode_Cmd,0x10);   //设置显示位置-列高地址

      for(i = 0;i&lt; 128; i++)
      {
            OLED_Write_Operate(OLED_Mode_Data,0x00);//清屏
      }
    }
}



/****************************************************************************
* 名 称:void OLED_Set_Positon(void)
* 功 能:设置显示坐标
* 入口参数:x,y
* 出口参数:无
* 返回值:无
* 说 明:无
****************************************************************************/
void OLED_Set_Positon(u8 x,u8 y)
{

    OLED_Write_Operate(OLED_Mode_Cmd,0xB0 + y);                           //设置页地址
    OLED_Write_Operate(OLED_Mode_Cmd,((x &amp; 0xf0) &gt;&gt;4 ) | 0x10); //设置列高四位地址
    OLED_Write_Operate(OLED_Mode_Cmd,(x &amp; 0x0f) | 0x01);          //设置列低四位地址

}


/****************************************************************************
* 名 称:void OLED_Display_on(void)
* 功 能:显示打开
* 入口参数:无
* 出口参数:无
* 返回值:无
* 说 明:无
****************************************************************************/
void OLED_Display_on(void)
{

    OLED_Write_Operate(OLED_Mode_Cmd,0x8d); //写充电泵寄存器
    OLED_Write_Operate(OLED_Mode_Cmd,0x14); //打开充电泵
    OLED_Write_Operate(OLED_Mode_Cmd,0xAF); //打开OLED显示

}


/****************************************************************************
* 名 称:void OLED_Display_offvoid)
* 功 能:显示关闭
* 入口参数:无
* 出口参数:无
* 返回值:无
* 说 明:无
****************************************************************************/
void OLED_Display_off(void)
{

    OLED_Write_Operate(OLED_Mode_Cmd,0x8d); //写充电泵寄存器
    OLED_Write_Operate(OLED_Mode_Cmd,0x10); //关闭充电泵
    OLED_Write_Operate(OLED_Mode_Cmd,0xAE); //关闭OLED显示

}


/****************************************************************************
* 名 称:void OLED_Display_onechar(u8 x,u8 y,u8 character,u8 size)
* 功 能:显示单个字符
* 入口参数:
x ,y坐标位置
character 字符
size 字号大小
* 出口参数:无
* 返回值:无
* 说 明:无
****************************************************************************/
void OLED_Display_onechar(u8 x,u8 y,u8 character,u8 size)
{
    u8 c=0,i=0;
    c = character-&#39; &#39;;
    if(x &gt; MAX_COLUMN - 1)
    {
      x= 0;
      if(size == SIZE8)
      {
            y = y+1;
      }
      if(size == SIZE16)
      {
            y = y+2;
      }
    }
    if(size == SIZE16)
    {
      OLED_Set_Positon(x,y);
      for( i =0;i&lt; 8; i++)
      {
            OLED_Write_Operate(OLED_Mode_Data,F8X16);
      }
      OLED_Set_Positon(x,y+1);
      for( i =0;i&lt; 8; i++)
      {
            OLED_Write_Operate(OLED_Mode_Data,F8X16);
      }
    }
    else
    {
      OLED_Set_Positon(x,y+1);
      for( i =0;i&lt; 6; i++)
      {
            OLED_Write_Operate(OLED_Mode_Data,F6x8<i>);
      }
    }
}


/****************************************************************************
* 名 称:void OLED_Display_string(u8 x,u8 y,u8 *str,u8 size)
* 功 能:显示字符串
* 入口参数:
x ,y坐标位置
str 字符串
size 字号大小
* 出口参数:无
* 返回值:无
* 说 明:无
****************************************************************************/
void OLED_Display_string(u8 x,u8 y,u8 *str,u8 size)
{
    u8 i=0;
    while(str<i>!=&#39;\0&#39;)
    {
      OLED_Display_onechar(x,y,str<i>,size);
      x += 8;
      if(x &gt; 120)
      {
            x = 0;
            y += 2;
      }
      i++;
    }
}


/****************************************************************************
* 名 称:void OLED_Display_Chinese(u8 x,u8 y,u8 Sno)
* 功 能:显示单个字符
* 入口参数:
x ,y坐标位置
Sno 表示汉字数组中一个字的码所占的序号
* 出口参数:无
* 返回值:无
* 说 明:无
****************************************************************************/
void OLED_Display_Chinese(u8 x,u8 y,u8 Sno)
{
    u8 adder=0,i=0;
    OLED_Set_Positon(x,y);
    for(i = 0;i&lt; 16;i++)
    {
      OLED_Write_Operate(OLED_Mode_Data,Hzk<i>);
      adder += 1;
    }
OLED_Set_Positon(x,y+1);
    for(i = 0;i&lt; 16;i++)
    {
      OLED_Write_Operate(OLED_Mode_Data,Hzk<i>);
      adder += 1;
    }
}



/****************************************************************************
* 名 称:u32 OLED_POW(u8 m, u8 n)
* 功 能:m的n次方
* 入口参数:
m为底数,n为指数
* 出口参数:无
* 返回值:无
* 说 明:无
****************************************************************************/
u32 OLED_POW(u8 m, u8 n)
{
    u32 result=1;
    while(n--)
    {
      result *= m;
    }
    return result;
}


/****************************************************************************
* 名 称:void OLED_Display_Num(u8 x,u8 y,u32 num, u8 Len,u8 size)
* 功 能:显示单个字符
* 入口参数:
x ,y坐标位置
Sno 表示汉字数组中一个字的码所占的序号
* 出口参数:无
* 返回值:无
* 说 明:无
****************************************************************************/
void OLED_Display_Num(u8 x,u8 y,u32 num, u8 len,u8 size)
{
    u8 i=0;
    u8 endisplay=0;
    u8 temp=0;
    for(i = 0;i &lt; len;i++)
    {
      temp = (num / (OLED_POW(10,len-i-1)))%10;
      if(endisplay == 0 &amp;&amp; i &lt; len-1)
      {
            if(temp == 0)
            {
                OLED_Display_onechar(x + (size / 2) * i,y,&#39; &#39;,size);
                continue;
            }
            else
            {
                endisplay = 1;
            }
      }
      OLED_Display_onechar(x + (size / 2) * i,y,temp + &#39;0&#39;,size);
    }
}


/****************************************************************************
* 名 称:void OLED_Display_Picture(u8 x0, u8 y0, u8 x1, u8 y1, u8 BMP[])
* 功 能:显示一张图片
* 入口参数:
x0 ,y0,x1,y1图片的大小
BMP[] 图片的码
* 出口参数:无
* 返回值:无
* 说 明:无
****************************************************************************/
void OLED_Display_Picture(u8 x0, u8 y0, u8 x1, u8 y1, u8 BMP[])
{
    u8 i=0;
    u8 x,y;
if(y1 == 8)
{
   y= y1/ 8;
    }
    else
    {
      y = y1/ 8 + 1;
    }
    for(y = y0;y &lt; y1;y++)
    {
      OLED_Set_Positon(x0,y);
      for(x = x0;x&lt; x1; x++)
      {
            OLED_Write_Operate(OLED_Mode_Data,BMP);
      }
    }
}
</i></i></i></i></i></code></pre>

<p><i><i><i>&nbsp;</i></i></i></p>

<p><i><i><i>2)、hal_entry函数</i></i></i></p>

<pre>
<i><i><i>
<code>void hal_entry(void)
{
      rt_kprintf(&quot;\nHello RT-Thread!\n&quot;);
      OLED_Display_string(0,0,&quot;CPK-RA6M4 Board Evaluation&quot;,SIZE8);
      while (1)
      {
            rt_thread_mdelay(50);
            breathing_light();
      }
}</code></i></i></i></pre>

<p><i><i><i>测试现象</i></i></i></p>

<p><i><i><i></i></i></i></p>

<p><i><i><i>最后,整个代码工程会在最后一篇测评中附上git链接。</i></i></i></p>

freebsder 发表于 2022-5-19 23:20

<p>全是代码,谢谢分享!</p>

nmg 发表于 2022-5-23 15:40

<p>要是现在已经上传了,可以单独给出这个工程师文件夹链接</p>
页: [1]
查看完整版本: RTT&瑞萨高性能 CPK-RA6M4 开发板测评——SPI驱动OLED