一、硬件准备与连接
1、触摸屏参数
本次驱动使用的屏幕模块核心参数如下:
- 面板尺寸:2.4寸
- 分辨率:240×RGB×320,16位色深
- 显示驱动芯片:ST7789V
- 触摸屏类型:电容屏
- 触摸屏驱动芯片:FT6336U
- 通信接口:4线SPI(显示模块),IIC(触摸屏模块)
- 接口说明:由于RA4M2的硬件SPI模块引脚被用于调试接口(SWD),所以本项目使用芯片的SCI模块模拟SPI与屏幕通信
- 所需引脚:
SCK:SPI时钟引脚
TXD(MOSI):主机输出从机输入数据线,用于RA4M2向屏幕发送数据/命令
RXD(MISO):主机输入从机输出数据线。因屏幕为纯接收设备,无需向MCU回传数据,此引脚可不连接
CS:片选引脚,低电平有效
DC:数据/命令选择引脚,用于区分当前发送的是指令还是显示数据
RESET:硬件复位引脚,低电平有效
BLK:背光控制引脚,高电平点亮
T_CLK:电容触摸屏I2C时钟信号
T_RST:电容触摸屏复位信号
T_SDA:电容触摸屏I2C数据信号
2、硬件连接
根据上述参数,RA4M2与ST7735V屏幕的引脚连接方式如下表所示。
RA4M2与ST7735V(SCI)硬件连接对应表
ST7735V屏幕引脚 |
信号说明 | RA4M2连接引脚 | 备注与说明 |
GND |
接地 | GND | 共地 |
VCC |
电源(3.3V) | 3.3V | 接3.3V电源输出 |
SCL |
SCI/SPI时钟线 | P102 | 必须连接至启用SCI模块的SCK引脚。 |
SDA |
SCI/SPI数据线,主出从入 | P101 | 必须连接至启用SCI模块的TXD引脚,用于发送数据。 |
RES |
硬件复位 | P000 | 接任意GPIO,用于初始化前复位屏幕 |
DC |
数据/命令选择 | P001 | 接任意GPIO,控制发送的是数据还是命令 |
CS |
片选 | P015 | 接任意GPIO,用于选择屏幕 |
BLK |
背光控制 | 3.3V | 可接固定3.3V长亮,或接PWM引脚通过GPIO控制亮度 |
备注:其中,RES即RESET,SDA即MOSI,SCK即SCLK,MISO并没有使用。
根据上述参数,RA4M2与FT6336U触摸屏的引脚连接方式如下表所示。
RA4M2与FT6336U(SCI)硬件连接对应表
FT6336U触屏引脚 |
信号说明 | RA4M2连接引脚 | 备注与说明 |
T_CLK |
电容触摸屏I2C时钟信号 | P301 | 必须连接至启用SCI模块的SCL引脚。 |
T_RST |
电容触摸屏复位信号 | P402 | 接任意GPIO,用于初始化前复位触摸屏。 |
T_SDA |
电容触摸屏I2C数据信号 | P302 | 必须连接至启用SCI模块的SDA引脚,用于收发数据。 |
T_SDO |
电容屏空脚 | 空脚 | 在电容屏的状态下,这是个空脚,不用连接。 |
T_INT |
电容触摸屏中断信号 | 空脚 | 接任意GPIO,用于在屏幕被触摸中触发中断,可以不用。 |
二、IIC外设介绍
1、IIC协议核心
IIC(Inter-Integrated Circuit)是一种常用的串行通信协议,由两根信号线即可完成主设备与多个从设备之间的数据通信。
IIC总线主要包含两根线:
- SCL(Serial Clock Line)
时钟线,也可以理解为CLK或SCK,由主机产生时钟信号,用于同步通信节奏。
- SDA(Serial Data Line)
数据线,用于发送和接收数据。IIC的数据收发共用同一根SDA线,因此属于半双工通信。
与SPI不同,IIC没有单独的片选(CS)引脚。这并不意味着IIC只能连接一个设备,IIC可以一条总线挂载连接多个从设备。每个从设备都会拥有一个唯一地址,主机在通信开始时,会先发送目标从设备地址,只有地址匹配的设备才会响应后续数据。例如:触摸芯片地址:0x38、EEPROM地址:0x50、传感器地址:0x68,主机通过地址即可区分不同设备。
2、IIC协议时序解析
下面结合逻辑分析仪抓取的波形,对一次典型IIC通信过程进行分析。

本图展示的是主机向地址为0x38的触摸芯片写入数据,然后再读取数据的过程。这里可以参考上一篇关于SPI的介绍,就可以很好的理解了,此外这一个完整的包内包含了:
(1)开始信号Start(图中绿色椭圆框)
开始信号(Start)是IIC通信的起始标志。在SCL保持高电平期间,SDA从高电平变为低电平。每当总线上的设备检测到这个变化后,就知道主机准备开始一次新的通信。
(2)发送从机地址
开始信号后,主机会先发送从机地址。图中从机地址为:0x38,这个时候只有地址为0x38的从机才会应答,其他的从机都不再理会后面的内容。后面紧跟的是1位读写控制位(R/W)。这一位中,0表示写操作(Write),也就是数据从主机发往从机;1表示读操作(Read),也就是数据从从机发往主机。图中的第一组数据为:0x38+0,表示:主机准备向地址为0x38的设备写数据。后面的第二组数据为:0x38+1,表示:主机准备从地址为0x38的设备读取数据。
(3)数据包
紧跟着上面的就是发送或者读取的数据。图中发送的数据为:0xAF;读取的数据为:0x01。数据在SCL高电平阶段进行采样。
(4)ACK应答位
每发送完8位数据后,发送方会释放SDA,接收方则会在第九个时钟周期返回ACK。ACK的规则为,0:应答成功(ACK),1:无应答(NACK)。
因此,IIC实际上是“8位数据+1位应答”的通信结构。图中的多个“0”应答位表示从设备正确接收到了数据。最后读取完成后的“1”则是主机发送的NACK,表示数据读取结束,不再继续读取。
(5)重复起始信号SR
图中中间位置出现了一个SR(Repeated Start),也就是重复起始信号。他的作用是不释放总线,直接切换通信方向。
(6)停止信号Stop(黄色椭圆框)
停止信号(Stop)表示一次IIC通信结束。也就是当SCL在高电平期间,SDA从低电平变成高电平。当设备检测到停止信号后会释放总线,等待下一次通信。
3、IIC协议总结
IIC协议的核心特点可以概括为:
- 仅需两根线即可通信
- 支持多从机挂载
- 使用地址区分设备
- 支持读写双向通信
- 通信结构清晰:
开始→地址→读写→ACK→数据→ACK→停止
由于硬件资源占用少、通信逻辑简单,IIC被广泛应用于:触摸屏、EEPROM、RTC时钟、传感器、OLED屏幕、PMIC电源管理等设备通信中。
三、实验功能实现
1、代码文件分类
在上次的基础上,对代码进行整理修改

输入文件夹名,点击“完成”。

在新建的文件夹上右键,点击“新建”,点击“从模板创建文件”

输入文件名,然后点击完成,这样就完成了一个文件的添加。

按照上面的步骤增加如下的文件

这个时候编译出提示找不到文件,下面开始增加文件地址,右键点击项目名,点击“属性”

点击“C/C++常规”,点击“路径和符号”,点击“包含”,点击“添加”

点击工作空间

选择新增的文件夹,点击确定

点击确定

点击“库路径”,点击添加

选择新增的文件夹,点击确定

点击确定

点击应用并关闭

点击“源位置”,点击“添加文件夹”

选择文件夹,点击确定

点击“重建索引”

到此,文件添加完毕。
2、IIC初始化
初始化程序如下:
双击“configuration.xml”

点击pins,点击SCI,点击SCI2,选择Simple I2C

点击Stacks,点击New,点击Connectivity,点击I2C_Master

点击属性,选择如下

点击生成

输入初始化代码
/*
* iic.c
*
* Created on: 2026年5月7日
* Author: w1832
*/
#include "iic.h"
volatile bool g_i2c_complete = false;
volatile bool g_i2c_error = false;
void sci_i2c_master_callback(i2c_master_callback_args_t *p_args)
{
switch(p_args->event)
{
case I2C_MASTER_EVENT_TX_COMPLETE:
case I2C_MASTER_EVENT_RX_COMPLETE:
{
g_i2c_complete = true;
break;
}
case I2C_MASTER_EVENT_ABORTED:
{
g_i2c_error = true;
break;
}
default:
{
break;
}
}
}
void IIC_Init(void)
{
R_SCI_I2C_Open(&g_i2c_ctrl, &g_i2c_cfg);
}
fsp_err_t Touch_Read_Reg(uint8_t reg,uint8_t *data,uint32_t len)
{
fsp_err_t err;
g_i2c_complete = false;
g_i2c_error = false;
/* 先写寄存器地址 */
err = R_SCI_I2C_Write(&g_i2c_ctrl,®,1,true);
if (FSP_SUCCESS != err)
{
return err;
}
while ((!g_i2c_complete) && (!g_i2c_error));
if (g_i2c_error)
{
return FSP_ERR_ABORTED;
}
/* 再读取数据 */
g_i2c_complete = false;
g_i2c_error = false;
err = R_SCI_I2C_Read(&g_i2c_ctrl,data,len,false);
if (FSP_SUCCESS != err)
{
return err;
}
while ((!g_i2c_complete) && (!g_i2c_error));
if (g_i2c_error)
{
return FSP_ERR_ABORTED;
}
return FSP_SUCCESS;
}
3、触屏功能实现
代码如下
/*
* touch.c
*
* Created on: 2026年5月7日
* Author: w1832
*/
#include "touch.h"
void Touch_Init(void)
{
TOUCH_RESET_ON;
R_BSP_SoftwareDelay (20U, BSP_DELAY_UNITS_MICROSECONDS);
TOUCH_RESET_OFF;
R_BSP_SoftwareDelay (300U, BSP_DELAY_UNITS_MICROSECONDS);
IIC_Init();
}
int Touch_i2c_read(uint8_t *point_num,touch_coord_t *touch_coord,uint8_t max_num)
{
uint8_t data_buf[5];
*point_num = 0;
if ((point_num == NULL) ||
(touch_coord == NULL) ||
(max_num == 0))
{
return -1;
}
/* 读取触摸点数量 */
if (Touch_Read_Reg(FT6X36_TD_STAT_REG,
&data_buf[0],
1) != FSP_SUCCESS)
{
return -1;
}
data_buf[0] &= FT6X36_TD_STAT_MASK;
if ((data_buf[0] == 0) || (data_buf[0] > 2))
{
if (data_buf[0] > 2)
{
return -2;
}
else
{
return 0;
}
}
/* 读取第一个点 */
Touch_Read_Reg(FT6X36_P1_XH_REG,
&data_buf[1],
4);
touch_coord[0].coord_x =
((data_buf[1] & FT6X36_MSB_MASK) << 8)
| data_buf[2];
touch_coord[0].coord_y =
((data_buf[3] & FT6X36_MSB_MASK) << 8)
| data_buf[4];
*point_num = 1;
/* 第二个点 */
if ((data_buf[0] > 1) && (max_num > 1))
{
Touch_Read_Reg(FT6X36_P2_XH_REG,
&data_buf[1],
4);
touch_coord[1].coord_x =
((data_buf[1] & FT6X36_MSB_MASK) << 8)
| data_buf[2];
touch_coord[1].coord_y =
((data_buf[3] & FT6X36_MSB_MASK) << 8)
| data_buf[4];
*point_num = 2;
}
return 0;
}
4、测试程序
代码如下
#include "hal_data.h"
#include "lcd.h"
#include "touch.h"
void hal_entry(void)
{
uint8_t point_num=0;
touch_coord_t touch[2];
R_IOPORT_Open(&g_ioport_ctrl, &g_bsp_pin_cfg);
SPI_Init();
Touch_Init();
Lcd_Init();
set_background(0x0000);
set_background(0x0f1c);
while(1)
{
if(Touch_i2c_read(&point_num,touch,2) == 0)
{
if(point_num > 0)
{
Show_asc16(0,0,0xFFFF,0x0000,touch[0].coord_x);
Show_asc16(30,30,0xFFFF,0x0000,touch[0].coord_y);
}
}
R_BSP_SoftwareDelay(20,BSP_DELAY_UNITS_MILLISECONDS);
}
}
测试结果

四、总结
1、总结
成功完成了触屏功能的测试
2、遇到问题和解决方案
本次遇到了一个很奇怪的问题,就是IO引脚不受控制,现象非常奇怪,且启用不同的引脚现象也不一样,最终排查为应该用上面的代码进行初始化,用下面的有概率初始化错误。
R_IOPORT_Open(&g_ioport_ctrl, &g_bsp_pin_cfg);
R_IOPORT_Open(&g_ioport_ctrl, NULL);
完整代码可以在附件获取,欢迎批评指正。


全部评论