29 SPI OLED屏幕显示实验

开发板官网(https://www.edevkit.com/)
本系列教程目录:
- 瑞萨单片机开发教程[一] e2-studio + DAP LINK开发环境搭建
- 瑞萨单片机开发教程[二] Keil/RASC开发环境搭建
- 瑞萨单片机开发教程[三] 新建RASC工程
- 瑞萨单片机开发教程[四] GPIO输出——led实验
- 瑞萨单片机开发教程[五] GPIO输入——按键key实验
- 瑞萨单片机开发教程[六] ICU外部中断实验
- 瑞萨单片机开发教程[七] SCI UART串口通信实验
- 瑞萨单片机开发教程[八] DMAC 串口数据传输实验
- 瑞萨单片机开发教程[九] ADC 电压采集实验
SPI LCD 屏幕显示实验
本章目标:
- 了解 RA6M5 处理器的 SPI 模块
- 学会使用 RASC 配置 SPI 模块,使用 API 读写数据。
硬件设计(略,参见配套硬件原理图)
软件设计
新建工程
复制一份“07 SCI-UART_Recv_send”修改文件夹名“29 SPI_OLED”,双击打开 EBF_RA6M5.uvprojx 工程,通过 Keil 的新建文件新建 spi_oled.c 和 spi_oled.h 保存到工程目录下的 src 文件夹中。
FSP 配置
Keil 界面选择 “Tools”→“RA Smart Configurator” 打开 RASC 界面。
根据原理图 LCD 引脚分布,包括一组 SPI 引脚,还需要 LCD_CS、LCD_RS、LCD_RST、LCD_BLK 四个控制引脚。
配置 SPI 引脚。

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

添加 SPI Stack 模块。

配置 SPI Stack 参数。

| name | 模块实例名 |
|---|---|
| Channel | 通道。 |
| Receive Interrupt Priority | 接收中断优先级 |
| Transmit Buffer Empty Interrupt Priority | 发送缓存区空中断优先级 |
| Transfer Complete Interrupt Priority | 发送完成中断优先级 |
| Error Interrupt Priority | 错误中断优先级 |
| Operating Mode | 操作模式。可选 SPI 主机或从机 |
| Clock Phase | SPI 时钟相位(设置 CPHA) |
| Clock Polarity | SPI 时钟极性(设置 CPOL) |
| Mode Fault Error | 模式错误检测。检测主从模式冲突 |
| Bit Order | 位时序。MSB 或 LSB |
| Callback | 中断回调函数。设置为 spi_flash_callback |
| SPI Mode | SPI 模式。设置为 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 Delay | SSL 失效延迟 |
| Next Access Delay | 下一次访问延迟 |
配置完成之后可以按下快捷键 “Ctrl + S” 保存,最后点击 “Generate Project Content” 按钮,让软件自动生成配置代码即可。
程序设计
通过 SPI 写入配置 LCD 屏初始化参数,实现画点、画线、画矩形函数,通过画矩形设置整个屏幕的颜色,再循环切换显示不同颜色。
- 打开 SPI 设备
fsp_err_t R_SPI_Open(spi_ctrl_t * p_ctrl,
spi_cfg_t const * const p_cfg);
- 读 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);
- 写 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);
- 中断回调函数
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
}
下载验证
连接下载器,将程序编译并下载到开发板之后,按下复位按键来复位开发板,程序启动初始化完。
屏幕背景初始化为白色,显示两行不同字体大小字符串。