第15章_瑞萨MCU零基础入门系列教程之Common I2C总线模块

本教程基于韦东山百问网出的 DShanMCU-RA6M5开发板 进行编写,需要的同学可以在这里获取: https://item.taobao.com/item.htm?id=728461040949

配套资料获取:https://renesas-docs.100ask.net

瑞萨MCU零基础入门系列教程汇总: https://blog.csdn.net/qq_35181236/article/details/132779862


第15章 Common I2C总线模块

本章目标

  • 使用RASC快速配置Common I2C模块
  • 学会使用i2c的API驱动触摸芯片,获取触点数据

15.1 Common I2C模块的使用

RA芯片的I2C分为Simple I2C和Common I2C。Simple I2C就是本书《第8章 SCI SPI》所讲的SCI模块的I2C模式,是使用串行总线来模拟I2C协议,而本章所讲的Common I2C是芯片内部实际存在的一个硬件I2C控制器模块。

得益于FSP的封装,Simple I2C和Common I2C在应用上并没有很大的差别。

15.1.1 I2C模块的配置

要配置I2C模块,先在RASC的“Pin Configuration”里的“Peripherals”找到“Connectivity:IIC”,然后根据硬件设计选择I2C通道。比如本书使用的是P409/P410作为I2C的SDA和SCL,这两个IO属于I2C2的A组引脚,因而选择“IIC2”,然后在展开的引脚配置中的“Pin Group Selection”选择“_A_only”并且使能操作模式,如下图所示:

接着再去“Stacks”里添加I2C的模块。点击“New Stack”,选择“Connectivity”,再选择里面的“I2C Master(r_iic_master)”。本章目标是作为主机去读取触摸屏的数据,所以选择Master,如下图所示:

当添加了I2C的Master模块后,就要去配置它的参数来。本章实验在RASC中配置I2C的参数具体如下图所示:

  • Name:I2C模块的名称,需要满足C语言字符串标准;
  • Channel:I2C模块的通道;
  • Rate:I2C通信速率,Standard支持的最大速率400kbps,快速模式最大能达到1Mbps;
  • Rise/Fall Time:SCL信号上升沿和下降沿的耗时;

  • Duty Cycle:SCL时钟线的占空比,范围是4%~96%,默认是50%;
  • Slave Address:从机设备地址,根据从机芯片设置;
  • Address Mode:地址模式,支持7-Bit和10-Bit;
  • Timeout Mode:数据检测超时模式,支持long mode和short mode。long mode的超时计数器是16bit的,short mode的超时计数器是14bit的;当超时计数溢出都没有检测到数据则通信中止;
  • Timeout during SCL Low:在SCL低电平时是否使能超时检测,默认是Enabled;
  • Callback:中断回调函数名称,建议和通道匹配,例如i2c1_callback;
  • Interrupt Priority Level:I2C中断优先级;

15.1.2 配置信息解读

配置信息分为两部分:引脚的配置信息、I2C模块的配置信息。

使用RASC配置好参数并生成工程后,会在工程的pin_data.c中生成模块的引脚信息,在hal_data.c中生成模块的配置信息。

  1. I2C模块的引脚信息

I2C涉及的引脚,它们的配置信息在工程的pin_data.c中生成。在RASC里配置的每一个引脚,都会在pin_data.c生成一个ioport_pin_cfg_t数组项,里面的内容跟配置时选择的参数一致。代码如下:

const ioport_pin_cfg_t g_bsp_pin_cfg_data[] = {......(省略内容){ .pin = BSP_IO_PORT_04_PIN_09,.pin_cfg = ((uint32_t) IOPORT_CFG_DRIVE_MID | (uint32_t) IOPORT_CFG_PERIPHERAL_PIN | (uint32_t) IOPORT_PERIPHERAL_IIC)},{ .pin = BSP_IO_PORT_04_PIN_10,.pin_cfg = ((uint32_t) IOPORT_CFG_DRIVE_MID | (uint32_t) IOPORT_CFG_PERIPHERAL_PIN | (uint32_t) IOPORT_PERIPHERAL_IIC)},......(省略内容)
};

这个常量数组将P5409和P410配置为I2C外设复用功能。

  1. I2C模块的配置信息

I2C的配置信息会在hal_data.c中的i2c_master_cfg_t结构体类型常量g_i2c_master2_cfg中定义,代码如下:

const i2c_master_cfg_t g_i2c_master2_cfg =
{.channel             = 2,.rate                = I2C_MASTER_RATE_STANDARD,.slave               = 0x14,.addr_mode           = I2C_MASTER_ADDR_MODE_7BIT,......(省略内容).p_callback          = i2c2_callback,.p_context           = NULL,......(省略内容).ipl                 = (12),.p_extend            = &g_i2c_master2_extend,
};
  • 第03行:通道设置为2;
  • 第04行:通信速率设置为标准速率;
  • 第05行:从机地址是0x14;
  • 第06行:地址模式为7bit模式;
  • 第08行:设置中断函数名为i2c2_callback;

使用I2C的open函数时,就会使用这个常量来初始化I2C模块。

15.1.3 中断回调函数

在g_i2c_master2_cfg用到了名为“i2c1_callback”的函数,此函数仅在hal_data.h中进行了如下声明,但是没有实现:

#ifndef i2c1_callback
void i2c1_callback(i2c_master_callback_args_t * p_args);
#endif

需要用户实现这个函数,例如:

void i2c1_callback(i2c_master_callback_args_t * p_args)
{switch (p_args->event){}
}

这个中断回调函数的参数是i2c_master_callback_args_t结构体指针,此结构体的原型如下:

typedef struct st_i2c_master_callback_args
{void const       * p_context;      ///< Pointer to user-provided contexti2c_master_event_t event;          ///< Event code
} i2c_master_callback_args_t;

其中事件成员是一个枚举类型,囊括的事件类型有:

typedef enum e_i2c_master_event
{I2C_MASTER_EVENT_ABORTED     = 1,  ///< A transfer was abortedI2C_MASTER_EVENT_RX_COMPLETE = 2,  ///< A receive operation was completed successfullyI2C_MASTER_EVENT_TX_COMPLETE = 3   ///< A transmit operation was completed successfully
} i2c_master_event_t;

从此可以知道,触发I2C中断的原因有:发送完成、接收完成、传输中止。它们触发中断后,会调用回调函数以执行用户的代码。

15.1.4 API接口及其应用

在I2C模块的FSP库函数头文件r_i2c_master_api.h中,定义了I2C主机设备的操作函数结构体i2c_master_api_t,原型如下:

/** Interface definition for I2C access as master */
typedef struct st_i2c_master_api
{fsp_err_t (* open)(i2c_master_ctrl_t * const p_ctrl, i2c_master_cfg_t const * const p_cfg);fsp_err_t (* read)(i2c_master_ctrl_t * const p_ctrl, uint8_t * const p_dest, uint32_t const bytes,bool const restart);fsp_err_t (* write)(i2c_master_ctrl_t * const p_ctrl, uint8_t * const p_src, uint32_t const bytes,bool const restart);fsp_err_t (* abort)(i2c_master_ctrl_t * const p_ctrl);fsp_err_t (* slaveAddressSet)(i2c_master_ctrl_t * const p_ctrl, uint32_t const slave,i2c_master_addr_mode_t const addr_mode);fsp_err_t (* callbackSet)(i2c_master_ctrl_t * const p_api_ctrl, void (* p_callback)(i2c_master_callback_args_t *),void const * const p_context, i2c_master_callback_args_t * const p_callback_memory);fsp_err_t (* statusGet)(i2c_master_ctrl_t * const p_api_ctrl, i2c_master_status_t * p_status);fsp_err_t (* close)(i2c_master_ctrl_t * const p_ctrl);
} i2c_master_api_t;

I2C主机设备支持的操作有:open/read/write/close等,FSP在r_iic_master.c中实现了这个结构体:

/* IIC Implementation of I2C device master interface */
i2c_master_api_t const g_i2c_master_on_iic =
{.open            = R_IIC_MASTER_Open,.read            = R_IIC_MASTER_Read,.write           = R_IIC_MASTER_Write,.abort           = R_IIC_MASTER_Abort,.slaveAddressSet = R_IIC_MASTER_SlaveAddressSet,.close           = R_IIC_MASTER_Close,.statusGet       = R_IIC_MASTER_StatusGet,.callbackSet     = R_IIC_MASTER_CallbackSet
};

本章以对I2C主机设备的打开关闭和读写为例进行分析。

  1. 打开I2C主机设备

打开I2C主机设备的函数指针原型如下:

/** Opens the I2C Master driver and initializes the hardware.* @param[in] p_ctrl    Pointer to control block. Must be declared by user. Elements are set here.* @param[in] p_cfg     Pointer to configuration structure.*/
fsp_err_t (* open)(i2c_master_ctrl_t * const p_ctrl, i2c_master_cfg_t const * const p_cfg);
  • p_ctrl:指向I2C主机控制块,比如g_i2c_master2_ctrl;
  • p_cfg:指向I2C主机参数配置结构体常量,比如g_i2c_master2_cfg;

p_ctrl的类型是iic_master_instance_ctrl_t结构体,在I2C通信的时候会改变此结构体中的设备状态、地址值、读写状态和应答状态等。

p_cfg的类型是i2c_master_cfg_t,此结构体被用来表示I2C主机的配置,例如I2C的通道、中断号、收发函数和中断回调函数等。

用户可以参照如下代码来打开I2C主机设备,它的内部会进行初始化:

fsp_err_t err = g_i2c_master2.p_api->open(g_i2c_master2.p_ctrl, g_i2c_master2.p_cfg);
if (FSP_SUCCESS != err)
{printf("%s %d\r\n", __FUNCTION__, __LINE__);return;
}
  1. 关闭I2C主机设备

关闭I2C设备的函数指针原型如下:

fsp_err_t (* close)(i2c_master_ctrl_t * const p_ctrl);

它的参数p_ctrl指向I2C主机控制块,此函数会将控制块中的I2C状态改变为关闭状态。

  1. I2C接收数据函数

I2C接收数据的函数指针原型如下:

fsp_err_t (* read)(i2c_master_ctrl_t * const p_ctrl, uint8_t * const p_dest, uint32_t const bytes,bool const restart);
  • p_ctrl:指向I2C主机设备控制块;
  • p_dest:目的数据(用来接收数据)的地址;
  • bytes:要接收的数据个数,单位是字节;
  • restart:主机接收完一帧数据后的操作,true-接收完一帧数据后主机不发送停止信号而是发送Start信号继续传输,false-接收完一帧数据后主机发送停止信号。

开发者可以参考如下代码来读取数据:

fsp_err_t err = g_i2c_master2.p_api->read(g_i2c_master2.p_ctrl, buf, len, 0);
if (FSP_SUCCESS != err)
{printf("%s %d\r\n", __FUNCTION__, __LINE__);return;
}
  1. I2C发送数据函数

I2C主机设备向从机设备发送数据的函数指针原型如下:

    fsp_err_t (* write)(i2c_master_ctrl_t * const p_ctrl, uint8_t * const p_src, uint32_t const bytes,bool const restart);
  • p_ctrl:执行I2C主机设备控制块,主机发送数据的时候会根据控制块的地址等信息发起开始信号、应答信号等;
  • p_dest:目的数据(用来接收数据)的地址;
  • bytes:要发送的数据个数,单位是字节;
  • restart:发送完此帧数据后的操作:true表示不会发出Stop信号而是马上发出Start信号——这样可以一直霸占I2C总线,false表示发出Stop信号(大家重新竞争I2C总线);

开发者可以参考如下代码来进行I2C的数据发送:

fsp_err_t err = g_i2c_master2.p_api->write(g_i2c_master2.p_ctrl, tmpbuf, 2, 0);
if (FSP_SUCCESS != err)
{printf("%s %d\r\n", __FUNCTION__, __LINE__);return;
}

15.2 Common I2C驱动触摸屏实验

15.2.1 硬件连接

本章使用的是外接触摸屏,使用FPC排线与主板相连,FPC的I2C原理图如下图所示:

使用的引脚是P409和P410。

15.2.2 GT911驱动解析

GT911是一款拥有5点电容触摸点位、拥有26个驱动通道和14个感应通道的触控芯片,可以同时识别5个触摸点位的实时准确位置、移动轨迹和触摸面积,并且根据主控需要,读取相应点为的触摸信息。

GT911的通信是标准的I2C通信协议,主机在和GT911进行I2C通信的时候需要满足I2C总线的标准协议。GT911的I2C从机设备地址定义如下图所示:

它支持两种地址,使用哪个地址取决于GT911发生复位后INT引脚的电平。如果复位时,INT引脚是高电平,则地址是0x14/0x28/0x29;否则就是0x5D,0xBA/0xBB。

本章实验设计选择的是地址0x14。

通过发送指令和读写数据来驱动GT911,不同的指令支持的数据个数不同:一个指令对应一个寄存器的数据,也可能一个指令对应N个寄存器的数据。以读取点位数据指令0x8157为例,用户发送0x8157指令后可以连续读取7个字节的数据(触控点位ID和触控位置信息):

15.2.3 GT911驱动程序

本章实验仅仅是简单地获取触点的位置信息。对于触摸设备,因对它的基本要求无非就是获取触摸点位信息,因而本章将抽象为“触摸设备”,在drv_touch.h中用一个结构体来描述这一类触摸设备:

typedef struct TouchDev{char *name;void (*Init)(struct TouchDev *ptDev);bool (*Read)(struct TouchDev *ptDev, unsigned short *pX, unsigned short *pY);
}TouchDev, *PTouchDev;

对于这一类触摸设备,应用层对它的操作只涉及:初始化、读取触摸位置。因而TouchDev结构体里只有Init、Read两个函数指针。

而对于具体的触摸驱动芯片,需要实现自己的TouchDev结构体。本章在drv_gt911.c中进行了实现TouchDev结构体,接下来对它的函数进行一一说明。

  1. 中断回调函数

在I2C通信过程中,需要上一次的传输完成后才能开始下一次传输,因而需要通过I2C的中断触发事件来判断上一次传输是否已经完成,代码如下:

static volatile bool gI2C2TxCplt = false;
static volatile bool gI2C2RxCplt = false;
void i2c2_callback(i2c_master_callback_args_t * p_args)
{switch (p_args->event){case I2C_MASTER_EVENT_TX_COMPLETE:{gI2C2TxCplt = true;break;}case I2C_MASTER_EVENT_RX_COMPLETE:{gI2C2RxCplt = true;break;}default:{gI2C2TxCplt = gI2C2RxCplt = false;break;}}
}
  • 第07~11行:如果触发中断的事件类型是发送完成事件,则将发送完成标志位置true;
  • 第12~16行:如果触发中断的事件类型是接收完成事件,则将接收完成标志位置true;
  1. 收发超时等待函数

本章实现了2个等待函数,里面加入了超时机制,代码如下:

static void I2C2WaitTxCplt(void)
{uint16_t wTimeOut = 100;while(!gI2C2TxCplt && wTimeOut){HAL_Delay(1);wTimeOut--;}gI2C2TxCplt = false;
}static void I2C2WaitRxCplt(void)
{uint16_t wTimeOut = 100;while(!gI2C2RxCplt && wTimeOut){HAL_Delay(1);wTimeOut--;}gI2C2RxCplt = false;
}
  1. 写GT911寄存器函数

写GT911的寄存器时,要发出寄存器地址、寄存器数据。数据个数可能各有不同,函数里动态分配内存,用来保存寄存器地址、寄存器数据,然后一次性发送出去。参考如下代码:

static void GT911DrvWriteReg(uint16_t reg, uint8_t *buf, uint8_t len)
{uint8_t regl = (uint8_t)(reg & 0xff);uint8_t regh = (uint8_t)(reg>>8);uint8_t * write_package = (uint8_t*)malloc((len + 2) * sizeof(uint8_t));memcpy(write_package, &regh, 1);memcpy(write_package + 1, &regl, 1);memcpy(write_package + 2, buf, len);fsp_err_t err = g_i2c_master2.p_api->write(g_i2c_master2.p_ctrl, write_package, len + 2, 0);if (FSP_SUCCESS != err){printf("%s %d\r\n", __FUNCTION__, __LINE__);return;}I2C2WaitTxCplt();free(write_package);
}
  • 第05~08行:根据传入的数据长度,动态分配数据包,并将指令和数据进行打包组合,以便调用I2C的write函数一次性发送出去;
  • 第09行:调用write函数发送数据包,发送完之后不再接着发送数据,因而最后一个参数restart=0;
  1. 读GT911寄存器函数

读寄存器时,要先发出寄存器地址,再读入数据。代码如下:

static void GT911DrvReadReg(uint16_t reg, uint8_t *buf, uint8_t len)
{uint8_t tmpbuf[2];tmpbuf[0] = (uint8_t)(reg >> 8);tmpbuf[1] = (uint8_t)(reg &0xff);fsp_err_t err = g_i2c_master2.p_api->write(g_i2c_master2.p_ctrl, tmpbuf, 2, 0);if (FSP_SUCCESS != err){printf("%s %d\r\n", __FUNCTION__, __LINE__);return;}I2C2WaitTxCplt();err = g_i2c_master2.p_api->read(g_i2c_master2.p_ctrl, buf, len, 0);if (FSP_SUCCESS != err){printf("%s %d\r\n", __FUNCTION__, __LINE__);return;}I2C2WaitRxCplt();
}
  1. 各类ID读取函数

GT911有多种ID供用户获取,以获取GT911的生产ID为例,它需要主机发送寄存器指令0x8140给GT911,然后读取4个字节的ID数据,代码如下:

static uint32_t GT911DrvReadProductID(void)
{uint32_t id = 0;GT911DrvReadReg(GT911_PRODUCT_ID_REG, (uint8_t*)&id ,4);return id;
}

其它的ID也可以参考这样的写法实现,本章的工程1501_i2c_touch为读者封装了这几个ID读取函数:

static uint32_t GT911DrvReadProductID(void);
static uint32_t GT911DrvReadVendorID(void);
static uint8_t GT911DrvReadVersion(void);
static uint8_t GT911DrvGetGSTID(void);
  1. 清除点位寄存器

在每次读取了触摸点位寄存器的数据后,需要将寄存器的数据清除掉,以便下一次触摸时更新寄存器的数据。如果不清除坐标寄存器的数据的话,每次读取都会得到一个固定值0x7F。

清除坐标寄存器的地址是0x814E,用户只需要往这个寄存器写入一个零即可,代码如下:

static void GT911DrvClearBuf(void)
{uint8_t data = {0};GT911DrvWriteReg(GT911_CLEARBUF_REG, (uint8_t*)&data, 1);
}
  1. 抽象GT911触摸屏设备对象

对于GT911触摸设备,本章根据其参数特性进行了封装,来表示GT911的触摸区域和触摸点位,参考以下在drv_gt911.h中的代码设计:

typedef enum{TP_ROT_NONE = 0,TP_ROT_90,TP_ROT_180,TP_ROT_270
} TouchRotation_t;/**用于存放每一个触控点的id,坐标,大小**/
typedef struct TouchPointInfo{unsigned char id;unsigned short x;unsigned short y;unsigned short size;
}TouchPointInfo_t;/**类结构体**/
typedef struct TouchDrv{unsigned char  ucAddr;unsigned short wHeight;unsigned short wWidth;TouchRotation_t tRotation;TouchPointInfo_t tPointsInfo[TOUCH_POINT_TOTAL]; //用于存储五个触控点的坐标
}TouchDrv_t;

在后续的设计中,通过定义结构体TouchDrv_t变量来表示一个GT911设备:

static struct TouchDrv gTP;
  1. 读取GT911触摸点位函数

在分析GT911的数据读写时,曾以读取某一个点位的数据为例,了解了一个点位拥有7个数据信息。而GT911共有5个点位信息可以获取,对应的寄存器地址在drv_gt911.h中以宏定义的形式体现:

#define GT_TP1_REG      0X814F      //第一个触摸点数据地址
#define GT_TP2_REG      0X8157      //第二个触摸点数据地址
#define GT_TP3_REG      0X815F      //第三个触摸点数据地址
#define GT_TP4_REG      0X8167      //第四个触摸点数据地址
#define GT_TP5_REG      0X816F      //第五个触摸点数据地址

什么情况下才需要去读取点位信息呢?有触摸事件发生的时候。而用户该如何获知GT911是否有被触摸呢?它用一个寄存器来表示:

  • Bit-7:buffer_status,1-有触摸数据等待主机读取;0-没有数据;
  • Bit-6:large detect,1-表示有大片区域被触摸了;
  • Bit-4:HaveKey,1-正在被触摸;0-没有被触摸或者松开触摸了;
  • Bit-[3:0]:触摸的点数;

用户可以根据这6个寄存器指令来获取每次触摸的点位信息了,参考如下代码:

static bool GT911DrvIsTouched(TouchDrv_t * tp)
{uint8_t touched_state, touch_num, buffer_status;touched_state = GT911DrvGetGSTID();touch_num = touched_state & 0xf;            //触点数量buffer_status = (touched_state >> 7) & 1;   // 帧状态if(buffer_status == 1 && (touch_num <= TOUCH_POINT_TOTAL) && (touch_num > 0)){uint16_t pointers_regs[TOUCH_POINT_TOTAL] = {GT_TP1_REG, GT_TP2_REG, GT_TP3_REG, GT_TP4_REG, GT_TP5_REG};// 获取每个触控点的坐标值并保存for (int i = 0; i < touch_num; ++i){uint8_t point_info_per_size = 7;uint8_t * point_info_p = malloc(point_info_per_size * sizeof(uint8_t ));GT911DrvReadReg(pointers_regs[i], point_info_p, point_info_per_size);tp->tPointsInfo[i].id = point_info_p[0];tp->tPointsInfo[i].x = (unsigned short)(point_info_p[1] + (point_info_p[2] << 8));tp->tPointsInfo[i].y = (unsigned short)(point_info_p[3] + (point_info_p[4] << 8));tp->tPointsInfo[i].size = (unsigned short)(point_info_p[5] + (point_info_p[6] << 8));free(point_info_p);//旋转方向uint16_t temp;switch (tp->tRotation){case TP_ROT_NONE:tp->tPointsInfo[i].x = tp->wWidth - tp->tPointsInfo[i].x;tp->tPointsInfo[i].y = tp->wHeight - tp->tPointsInfo[i].y;break;case TP_ROT_270:temp = tp->tPointsInfo[i].x;tp->tPointsInfo[i].x = tp->wWidth - tp->tPointsInfo[i].y;tp->tPointsInfo[i].y = temp;break;case TP_ROT_180:
//                    tp->tPointsInfo[i].x = tp->tPointsInfo[i].x;
//                    tp->tPointsInfo[i].y = tp->tPointsInfo[i].y;break;case TP_ROT_90:temp = tp->tPointsInfo[i].x;tp->tPointsInfo[i].x = tp->tPointsInfo[i].y;tp->tPointsInfo[i].y = tp->wHeight - temp;break;default:break;}}GT911DrvClearBuf();return true;}//必须给GT911_POINT_INFO缓冲区置0,不然读取的数据一直为128!!!!GT911DrvClearBuf();return false;
}

另外,为了和之前抽象出来的触摸设备(TouchDev)相匹配,还要在此基础上封装一个Read函数:

static bool GT911DrvRead(struct TouchDev *ptDev, unsigned short *pX, unsigned short *pY)
{if(NULL == ptDev->name) return false;if(GT911DrvIsTouched(&gTP)){*pX = gTP.tPointsInfo[0].x;*pY = gTP.tPointsInfo[0].y;return true;}return false;
}
  1. GT911初始化函数

本章实验并未对GT911进行更精细的设置,因而并不没有修改它的寄存器。

本章的初始化函数仅仅初始化I2C设备、读取GT911的ID和触摸范围,参考如下代码:

static void GT911DrvInit(struct TouchDev *ptDev)
{if(NULL == ptDev->name) return;uint8_t buf[4];gTP.ucAddr = (uint8_t)g_i2c_master2.p_cfg->slave;gTP.tRotation = TP_ROT_NONE;/* 初始化I2C驱动 */fsp_err_t err = g_i2c_master2.p_api->open(g_i2c_master2.p_ctrl, g_i2c_master2.p_cfg);if (FSP_SUCCESS != err){printf("%s %d\r\n", __FUNCTION__, __LINE__);return;}/* 读ID */uint32_t nVendorID = GT911DrvReadVendorID();printf("gt911 vendor id: 0x%.4x\r\n", (int)nVendorID);uint32_t nProductID = GT911DrvReadProductID();printf("gt911 product id: 0x%.4x\r\n", (int)nProductID);uint8_t nVersion = GT911DrvReadVersion();printf("version = 0x%x\r\n", nVersion);GT911DrvReadReg(0x8048, buf, 2);gTP.wWidth = (unsigned short)((buf[1] << 8) | buf[0]);GT911DrvReadReg(0x804A, buf, 2);gTP.wHeight = (unsigned short)((buf[1] << 8) | buf[0]);
}
  1. 触摸设备的注册和获取

以面向对象的思想,构造了一个TouchDev结构体gTouchDev,代码如下:

static struct TouchDev gTouchDev = {.name = "GT911",.Init = GT911DrvInit,.Read = GT911DrvRead
};

然后再以一个函数将此设备返回给上层应用:

struct TouchDev* TouchDevGet(void)
{return &gTouchDev;
}

15.2.4 测试程序

app_test.c是测试程序,它会打印触摸点的信息,代码如下:

void TouchAppTest(void)
{TouchDev *ptDev = TouchDevGet();if(NULL == ptDev){printf("Error. Not Found Touch Device!\r\n");return;}ptDev->Init(ptDev);uint16_t x = 0, y = 0;while(1){if(ptDev->Read(ptDev, &x, &y) == true){printf("Touch-Position: (%d,%d)\r\n", x, y);}}
}

15.2.5 上机实验

在hal_entry.c中的hal_entry()函数中,初始化滴答定时器、初始化调试串口,然后调用TouchAppTest函数进行测试。

当触摸屏幕的时候,串口助手就会打印例如下图这样的点位坐标信息:


本章完

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/129278.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

vue3+vite项目运行报错[plugin vite:dep-pre-bundle]

报错截图 原因&#xff1a;vue-i18n 插件于9.5更新&#xff0c;打包和引入方式有改变&#xff0c;所以新启动和新部署的项目都会有这个问题。 解决方式&#xff1a; 1.项目全局搜索&#xff1a;vue-i18n/dist/v ue-i18n.cjs.js 然后将搜索到的代码替换为 &#xff1a;vue-i18…

为特征向量数据(1D数组)叠加噪声实现数据增强

为特征向量数据&#xff08;1D数组&#xff09;叠加噪声实现数据增强 日期作者版本备注2023.09.11Dog TaoV1.0完成文档的初始版本。 文章目录 为特征向量数据&#xff08;1D数组&#xff09;叠加噪声实现数据增强背景介绍叠加噪声的主要方法高斯噪声&#xff08;Gaussian Nois…

微服务05-Docker基本操作

Docker的定义 1.什么是Docker Docker是一个快速交付应用、运行应用的技术&#xff1a; 可以将程序及其依赖、运行环境一起打包为一个镜像&#xff0c;可以迁移到任意Linux操作系统运行时利用沙箱机制形成隔离容器&#xff0c;各个应用互不干扰启动、移除都可以通过一行命令完…

超详细-Vivado配置Sublime+Sublime实现VHDL语法实时检查

目录 一、前言 二、准备工作 三、Vivado配置Sublime 3.1 Vivado配置Sublime 3.2 环境变量添加 3.3 环境变量验证 3.4 Vivado设置 3.5 配置验证 3.6 解决Vivado配置失败问题 四、Sublime配置 4.1 Sublime安装Package Control 4.2 Sublime安装VHDL插件 4.3 语法检查…

STM32F4X DMA

STM32F4X DMA 什么是DMASTM32F4X DMADMA框图DMA通道DMA仲裁器DMA FIFO DMA传输模式DMA传输方向存储器到存储器存储器到外设外设到存储器 DMA循环模式和普通模式循环模式&#xff08;Circular&#xff09;普通模式&#xff08;Normal&#xff09; DMA源、目标寄存器增量模式DMA例…

React【组件生命周期 、组件生命周期_挂载、 组件生命周期_更新 、组件生命周期_卸载、表单_受控组件、表单_受控组件处理多个输入】(三)

文章目录 组件生命周期 组件生命周期_挂载 组件生命周期_更新 组件生命周期_卸载 表单_受控组件 表单_受控组件处理多个输入 组件生命周期 每个组件都有自己的生命周期&#xff0c;从“生”到”死“。 在这个过程当中&#xff0c;它会有不同的状态&#xff0c;针对不同的状态…

大型语言模型的幻觉研究|减轻及避免大模型LLM幻觉(二)

“ 本文及上一篇综述了最近关于语言模型中幻觉问题的研究进展&#xff0c;主要集中在ChatGPT发布后的研究。文章讨论了如何评估、追踪和消除幻觉&#xff0c;并探讨了现有挑战和未来方向。希望本文能为对LLM幻觉问题感兴趣的朋友提供有价值的资源&#xff0c;促进LLM的实际应用…

【数学建模竞赛】超详细Matlab二维三维图形绘制

二维图像绘制 绘制曲线图 g 是表示绿色 b--o是表示蓝色/虚线/o标记 c*是表示蓝绿色(cyan)/*标记 ‘MakerIndices,1:5:length(y) 每五个点取点&#xff08;设置标记密度&#xff09; 特殊符号的输入 序号 需求 函数字符结构 示例 1 上角标 ^{ } title( $ a…

Aztec的隐私抽象:在尊重EVM合约开发习惯的情况下实现智能合约隐私

1. 引言 Aztec的架构&#xff0c;不同于当前“通过EVM兼容执行环境”所实现的区块链水平扩容趋势。Aztec内部笑称其构建的为首个非zkEVM协议。 Aztec专注于实现&#xff1a; 成为理解和需要智能合约隐私的开发者的终极解决方案。 Aztec为开发者提供构建隐私优先app所需的网…

Java 复习笔记 - 面向对象进阶篇

文章目录 一&#xff0c;Static&#xff08;一&#xff09;Static的概述&#xff08;二&#xff09;静态变量&#xff08;三&#xff09;静态方法&#xff08;四&#xff09;工具类&#xff08;五&#xff09;static的注意事项 二&#xff0c;继承&#xff08;一&#xff09;继…

TortoiseSVN 详细操作指南

文章底部有个人公众号&#xff1a;热爱技术的小郑。主要分享开发知识、有兴趣的可以关注一下。为何分享&#xff1f; 踩过的坑没必要让别人在再踩&#xff0c;自己复盘也能加深记忆。利己利人、所谓双赢。 热爱技术的小郑 1、引言 考虑以下几种情况&#xff1a; 你是否在一个…

Nginx详解 五:反向代理

文章目录 1. 正向代理和反向代理1.1 正向代理概述1.1.1 什么是正向代理1.1.2 正向代理的作用1.1.3 正向代理的基本格式 1.2 反向代理概述1.2.1 什么是反向代理1.2.2 反向代理可实现的功能1.2.3 反向代理的可用模块 2. 配置反向代理2.1 反向代理配置参数2.1.1 proxy_pass2.1.2 其…

华为云云耀云服务器L实例评测|老用户回归的初印象

华为云云耀云服务器L实例评测&#xff5c;老用户回归的初印象 前言一、新面孔1. 云耀云服务器2. 服务器特色 二、上手感官体验1. 性价比感受2. 推荐宝塔面板3. CloudShell登录4. 安全性 总结 前言 其实笔者接触华为云已经很久了&#xff0c;第一次使用的云服务器就是华为云。当…

C# 反射机制

图片来自&#xff1a;https://www.cnblogs.com/tangge/p/3440605.html

探索多态的本质【C++】

文章目录 多态的构成条件虚函数虚函数的重写&#xff08;覆盖&#xff09; 虚函数重写的两个例外C11 override和final区分重载、覆盖(重写)、隐藏(重定义)抽象类接口继承和实现继承多态的原理虚函数表 动态绑定和静态绑定动态绑定静态绑定 单继承中的虚函数表多继承中的虚函数表…

视频监控平台EasyCVR分组批量绑定/取消通道功能的后端代码设计逻辑介绍

视频监控平台/视频存储/视频分析平台EasyCVR基于云边端一体化管理&#xff0c;可支持视频实时监控、云端录像、云存储、磁盘阵列存储、回放与检索、智能告警、平台级联等功能。安防监控平台在线下场景中应用广泛&#xff0c;包括智慧工地、智慧工厂、智慧校园、智慧社区等等。 …

Ubuntu20.04同时安装ROS1和ROS2

Ubuntu20.04同时安装ROS1和ROS2 Excerpt 每版的Ubuntu系统版本都有与之对应ROS版本的&#xff0c;每一版ROS都有其对应版本的Ubuntu版本&#xff0c;不可随便装&#xff0c;ubuntu20.04对应ROS1 noetic和ROS2 foxy版本。_ros1和ros2共存 Ubuntu20.04同时安装ROS1和ROS2共存 文…

Vue+NodeJS+MongoDB实现邮箱验证注册、登录

一.主要内容 邮件发送用户注册用户信息存储到数据库用户登录密码加密JWT生成tokenCookie实现快速登录 在用户注册时,先发送邮件得到验证码.后端将验证进行缓存比对,如果验证码到期,比对不正确,拒绝登录;如果比对正确,将用户的信息进行加密存储到数据库. 用户登录时,先通过用…

[H5动画制作系列] Sprite及Text Demo

参考代码: sprite.js: var canvas, stage, container; canvas document.getElementById("mainView"); function init() {stage new createjs.Stage(canvas);createjs.Touch.enable(stage);var loader new createjs.LoadQueue(false);loader.addEventListener(&q…

Docker的开源容器镜像仓库Harbor安装

概述 Docker Hub是Docker官方提供的在线Docker镜像注册中心&#xff0c;其支持Docker镜像的查询&#xff08;search&#xff09;、提交&#xff08;push&#xff09;以及获取&#xff08;pull&#xff09;。目前&#xff0c;在云原生领域中&#xff0c;CNCF提供Harbor开源版本…