跳到主要内容

29 SPI OLED屏幕显示实验

在这里插入图片描述

开发板官网https://www.edevkit.com/)

本系列教程目录:


SPI LCD 屏幕显示实验

本章目标:

  • 了解 RA6M5 处理器的 SPI 模块
  • 学会使用 RASC 配置 SPI 模块,使用 API 读写数据。

硬件设计(略,参见配套硬件原理图)

软件设计

新建工程

复制一份“07 SCI-UART_Recv_send”修改文件夹名“29 SPI_OLED”,双击打开 EBF_RA6M5.uvprojx 工程,通过 Keil 的新建文件新建 spi_oled.cspi_oled.h 保存到工程目录下的 src 文件夹中。

FSP 配置

Keil 界面选择 “Tools”→“RA Smart Configurator” 打开 RASC 界面。

根据原理图 LCD 引脚分布,包括一组 SPI 引脚,还需要 LCD_CS、LCD_RS、LCD_RST、LCD_BLK 四个控制引脚。

配置 SPI 引脚。

插图01

配置 LCD_CS、LCD_RS、LCD_RST、LCD_BLK 四个控制引脚都为输出模式。

插图02

添加 SPI Stack 模块。

插图03

配置 SPI Stack 参数。

插图04

name模块实例名
Channel通道。
Receive Interrupt Priority接收中断优先级
Transmit Buffer Empty Interrupt Priority发送缓存区空中断优先级
Transfer Complete Interrupt Priority发送完成中断优先级
Error Interrupt Priority错误中断优先级
Operating Mode操作模式。可选 SPI 主机或从机
Clock PhaseSPI 时钟相位(设置 CPHA)
Clock PolaritySPI 时钟极性(设置 CPOL)
Mode Fault Error模式错误检测。检测主从模式冲突
Bit Order位时序。MSB 或 LSB
Callback中断回调函数。设置为 spi_flash_callback
SPI ModeSPI 模式。设置为 SPI Operation
Full or Transmit Only Mode全双工或仅发送模式选择
Slave Select Polarity从机选择引脚极性。一般是低电平有效
Select SSL(Slave Select)从机选择信号
MOSI Idle State总线空闲时 MOSI 电平
Parity Mode极性模式
Byte Swapping字节交换模式
Bitrate比特率
Clock Delay时钟延迟
SSL Negation DelaySSL 失效延迟
Next Access Delay下一次访问延迟

配置完成之后可以按下快捷键 “Ctrl + S” 保存,最后点击 “Generate Project Content” 按钮,让软件自动生成配置代码即可。

程序设计

通过 SPI 写入配置 LCD 屏初始化参数,实现画点、画线、画矩形函数,通过画矩形设置整个屏幕的颜色,再循环切换显示不同颜色。

  1. 打开 SPI 设备
fsp_err_t R_SPI_Open(spi_ctrl_t * p_ctrl,
spi_cfg_t const * const p_cfg);
  1. 读 SPI 数据
fsp_err_t R_SPI_Read(spi_ctrl_t * const p_ctrl,
void * p_dest,
uint32_t const length,
spi_bit_width_t const bit_width);
  1. 写 SPI 数据
fsp_err_t R_SPI_Write(spi_ctrl_t * const p_ctrl,
void * p_dest,
uint32_t const length,
spi_bit_width_t const bit_width);
  1. 中断回调函数
void spi0_callback(spi_callback_args_t *p_args)
{
if(p_args->event == SPI_EVENT_TRANSFER_COMPLETE)
{
g_transfer_complete = true;
}
}

程序编写

spi_lcd.h 头文件添加屏幕相关宏定义,定义 LCD 绘制相关函数。

#ifndef BSP_SPI_LCD_H_
#define BSP_SPI_LCD_H_

#include "hal_data.h"

#define XLevelL 0x00
#define XLevelH 0x10
#define XLevel ((XLevelH&0x0F)*16+XLevelL)
#define Max_Column 128
#define Max_Row 64
#define Brightness 0xCF
#define X_WIDTH 128
#define Y_WIDTH 64

//SPI cs引脚
#define LCD_CS_Pin BSP_IO_PORT_01_PIN_14
#define LCD_CS_Clr() R_IOPORT_PinWrite(&g_ioport_ctrl, LCD_CS_Pin, BSP_IO_LEVEL_LOW)
#define LCD_CS_Set() R_IOPORT_PinWrite(&g_ioport_ctrl, LCD_CS_Pin, BSP_IO_LEVEL_HIGH)

//SPI 数据/命令切换引脚
#define LCD_DC_Pin BSP_IO_PORT_01_PIN_13
#define LCD_DC_Clr() R_IOPORT_PinWrite(&g_ioport_ctrl, LCD_DC_Pin, BSP_IO_LEVEL_LOW)
#define LCD_DC_Set() R_IOPORT_PinWrite(&g_ioport_ctrl, LCD_DC_Pin, BSP_IO_LEVEL_HIGH)

//SPI 复位引脚
#define LCD_RST_Pin BSP_IO_PORT_01_PIN_12
#define LCD_RST_Clr() R_IOPORT_PinWrite(&g_ioport_ctrl, LCD_RST_Pin, BSP_IO_LEVEL_LOW)
#define LCD_RST_Set() R_IOPORT_PinWrite(&g_ioport_ctrl, LCD_RST_Pin, BSP_IO_LEVEL_HIGH)

//LCD控制用函数
void LCD_WrDat(unsigned char dat);//写数据
void LCD_WrCmd(unsigned char cmd);//写命令
void LCD_SetPos(unsigned char x, unsigned char y);//设置起始点坐标

//填充矩形
void LCD_Fill(uint16_t xsta,uint16_t ysta,uint16_t xend,uint16_t yend,uint16_t color);
void LCD_CLS(void);//清屏
void LCD_Init(void);//初始化

//显示6*8大小的字符串
void LCD_6x8Str(unsigned char x, unsigned char y, unsigned char ch[]);
//显示8*16大小的字符串
void LCD_8x16Str(unsigned char x, unsigned char y, unsigned char ch[]);

#endif

spi_lcd.c 中实现写数据、写命令和设置坐标相关函数,以及其余绘制相关函数。

#include "spi_lcd.h"
#include "codetab.h"

static volatile bool g_transfer_complete = false;//发送成功标志

//发送超时等待函数
static bool SPIWaitgtc(void)
{
unsigned short wTimeout = 100;
while(!g_transfer_complete && wTimeout)
{
R_BSP_SoftwareDelay(1u, BSP_DELAY_UNITS_MICROSECONDS);
wTimeout--;
}
g_transfer_complete = false;
if(g_transfer_complete==false && wTimeout==0)
return false;
return true;
}

//中断回调函数
void spi0_callback(spi_callback_args_t *p_args)
{
if(p_args->event == SPI_EVENT_TRANSFER_COMPLETE)
{
g_transfer_complete = true;
}
}

//LCD写命令
void LCD_WrCmd(unsigned char cmd)
{
R_IOPORT_PinWrite(&g_ioport_ctrl,BSP_IO_PORT_01_PIN_13,BSP_IO_LEVEL_LOW);
R_SPI_Write(&g_spi0_ctrl,&cmd,1,SPI_BIT_WIDTH_8_BITS);
SPIWaitgtc();
}

//LCD写数据
void LCD_WrDat(unsigned char dat)
{
R_IOPORT_PinWrite(&g_ioport_ctrl,BSP_IO_PORT_01_PIN_13,BSP_IO_LEVEL_HIGH);
R_SPI_Write(&g_spi0_ctrl,&dat,1,SPI_BIT_WIDTH_8_BITS);
SPIWaitgtc();
}

//写16位数据
void LCD_WR_DATA(uint16_t dat)
{
LCD_WrDat(dat>>8);
LCD_WrDat((uint8_t)dat);
}

//设置起始和结束值坐标
void LCD_Address_Set(uint16_t x1,uint16_t y1,uint16_t x2,uint16_t y2)
{
LCD_WrCmd(0x2a);//列地址设置
LCD_WR_DATA(x1);
LCD_WR_DATA(x2);
LCD_WrCmd(0x2b);//行地址设置
LCD_WR_DATA(y1);
LCD_WR_DATA(y2);
LCD_WrCmd(0x2c);//储存器写
}

//画矩形
void LCD_Fill(uint16_t xsta,uint16_t ysta,uint16_t xend,uint16_t yend,uint16_t color)
{
uint16_t i,j;
LCD_CS_Clr();
LCD_Address_Set(xsta,ysta,xend-1,yend-1);//设置显示范围

for(i=ysta;i<yend;i++)
{
for(j=xsta;j<xend;j++)
{
LCD_WR_DATA(color);
}
}
LCD_CS_Set();
}

//设置字符坐标
void LCD_SetPos(unsigned char x, unsigned char y)
{
LCD_WrCmd(0xb0 + y);
LCD_WrCmd(((x&0xf0)>>4)|0x10);
LCD_WrCmd((x&0x0f)|0x01);
}

//清屏
void LCD_CLS(void)
{
LCD_Fill(0,0,240,320,0x00);
}

//显示6x8的ASCII字符
void LCD_6x8Str(unsigned char x, unsigned char y, unsigned char ch[])
{
unsigned char c=0,i=0,j=0;
while (ch[j]!='\0')
{
c = ch[j]-32;
if(x>126)
{
x=0;y++;
}
LCD_SetPos(x,y);
for(i=0;i<6;i++)
{
LCD_WrDat(F6x8[c][i]);
}
x+=6;
j++;
}
}

//显示8x16的ASCII字符
void LCD_8x16Str(unsigned char x, unsigned char y, unsigned char ch[])
{
unsigned char c=0,i=0,j=0;
while (ch[j]!='\0')
{
c =ch[j]-32;
if(x>120)
{
x=0;y++;
}
LCD_SetPos(x,y);
for(i=0;i<8;i++)
{
LCD_WrDat(F8X16[c*16+i]);
}
LCD_SetPos(x,y+1);
for(i=0;i<8;i++)
{
LCD_WrDat(F8X16[c*16+i+8]);
}
x+=8;
j++;
}
}

//LCD显示屏初始化
void LCD_Init(void)
{
fsp_err_t err = FSP_SUCCESS;
err = R_SPI_Open(&g_spi0_ctrl,&g_spi0_cfg);//打开SPI模块
assert(FSP_SUCCESS == err);

//复位
LCD_RST_Clr();
R_BSP_SoftwareDelay(10u, BSP_DELAY_UNITS_MILLISECONDS);//延时10ms
LCD_RST_Set();
R_BSP_SoftwareDelay(120u, BSP_DELAY_UNITS_MILLISECONDS);//延时120ms

LCD_CS_Clr();
LCD_WrCmd(0x11);

R_BSP_SoftwareDelay(120u, BSP_DELAY_UNITS_MILLISECONDS);//延时120ms
LCD_WrCmd(0x36); // 内存数据访问控制
LCD_WrDat(0x00); // 方向配置(根据实际调整)

LCD_WrCmd(0x3A);
LCD_WrDat(0x05); // 16位色(0x05和0x06均兼容,确保绘图用RGB565)

// 3. 配置显示窗口(关键:限定240×320显示区域)
// 设置列地址(0~239)
LCD_WrCmd(0x2A);
LCD_WrDat(0x00); // 起始列高8位
LCD_WrDat(0x00); // 起始列低8位
LCD_WrDat(0x00); // 结束列高8位
LCD_WrDat(0xEF); // 结束列低8位(239)

// 设置行地址(0~319)
LCD_WrCmd(0x2B);
LCD_WrDat(0x00); // 起始行高8位
LCD_WrDat(0x00); // 起始行低8位
LCD_WrDat(0x01); // 结束行高8位(319 = 0x13F)
LCD_WrDat(0x3F); // 结束行低8位

// ************** 时序与电源配置 **************
LCD_WrCmd(0xB2); // Porch设置
LCD_WrDat(0x05);
LCD_WrDat(0x05);
LCD_WrDat(0x00);
LCD_WrDat(0x33);
LCD_WrDat(0x33);

LCD_WrCmd(0xB7); // Gate控制
LCD_WrDat(0x35);

LCD_WrCmd(0xBB); // VCOM电压设置
LCD_WrDat(0x21);

LCD_WrCmd(0xC0); // 电源控制
LCD_WrDat(0x2C);

LCD_WrCmd(0xC2); // VDV使能
LCD_WrDat(0x01);

LCD_WrCmd(0xC3); // VRH设置
LCD_WrDat(0x0B);

LCD_WrCmd(0xC4); // VDV电压设置
LCD_WrDat(0x20);

LCD_WrCmd(0xC6); // 帧率控制
LCD_WrDat(0x0F); // 60Hz

LCD_WrCmd(0xD0); // 电源控制1
LCD_WrDat(0xA4);
LCD_WrDat(0xA1);

LCD_WrCmd(0xD6); // 显示输出控制
LCD_WrDat(0xA1);

// ************** Gamma校准(优化颜色表现) **************
LCD_WrCmd(0xE0); // 正Gamma
LCD_WrDat(0xD0);
LCD_WrDat(0x04);
LCD_WrDat(0x08);
LCD_WrDat(0x0A);
LCD_WrDat(0x09);
LCD_WrDat(0x05);
LCD_WrDat(0x2D);
LCD_WrDat(0x43);
LCD_WrDat(0x49);
LCD_WrDat(0x09);
LCD_WrDat(0x16);
LCD_WrDat(0x15);
LCD_WrDat(0x26);
LCD_WrDat(0x2B);

LCD_WrCmd(0xE1); // 负Gamma
LCD_WrDat(0xD0);
LCD_WrDat(0x03);
LCD_WrDat(0x09);
LCD_WrDat(0x0A);
LCD_WrDat(0x0A);
LCD_WrDat(0x06);
LCD_WrDat(0x2E);
LCD_WrDat(0x44);
LCD_WrDat(0x40);
LCD_WrDat(0x3A);
LCD_WrDat(0x15);
LCD_WrDat(0x15);
LCD_WrDat(0x26);
LCD_WrDat(0x2A);

// ************** 显示控制 **************
LCD_WrCmd(0x21); // 关闭反显(正显模式)
LCD_WrCmd(0x29); // 开启显示

LCD_CS_Set(); // 片选使能(根据硬件接线确认)
}

hal_entry.c 文件中添加测试代码。

void hal_entry(void)
{
/* TODO: add your own code here */

led_Init(); // LED 初始化
uart0_Init();

printf("start LCD test\r\n");

//设置白色背景颜色
LCD_Fill(0,0,240,320,0x00);

LCD_6x8Str(0,1, (unsigned char*)"SPI LCD 6x8 string");
LCD_8x16Str(0,2, (unsigned char*)"SPI LCD 8x16 string");

while(1)
{
LED1_TOGGLE();
R_BSP_SoftwareDelay(1, BSP_DELAY_UNITS_SECONDS); //延时1秒
}


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

下载验证

连接下载器,将程序编译并下载到开发板之后,按下复位按键来复位开发板,程序启动初始化完。

屏幕背景初始化为白色,显示两行不同字体大小字符串。