当前位置: 首页 > news >正文

第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函数进行测试。

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


本章完

相关文章:

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

本教程基于韦东山百问网出的 DShanMCU-RA6M5开发板 进行编写&#xff0c;需要的同学可以在这里获取&#xff1a; https://item.taobao.com/item.htm?id728461040949 配套资料获取&#xff1a;https://renesas-docs.100ask.net 瑞萨MCU零基础入门系列教程汇总&#xff1a; ht…...

《TCP/IP网络编程》阅读笔记--多播与广播

目录 1--多播 2--多播代码实例 3--广播 4--广播代码实例 1--多播 多播方式的数据传输是基于 UDP 完成的&#xff0c;多播数据包的格式与 UDP 数据包相同&#xff1b; 多播与 UDP 的区别&#xff1a;UDP 数据传输以单一目标进行&#xff0c;多播数据同时传递到加入&#xff…...

聚观早报|华为Mate 60 Pro支持面容支付;特斯拉重回底特律车展

【聚观365】9月8日消息 华为Mate 60 Pro已支持面容支付 特斯拉将重回底特律车展 iPhone在美国有1.67亿用户 韩国半导体8月份出口85.6亿美元 比亚迪元PLUS冠军版将于9月15日上市 华为Mate 60 Pro已支持面容支付 毫无预热的华为Mate 60 Pro突然在华为商城首批开售&#xf…...

本地缓存Caffeine的缓存过期淘汰策略

本地缓存是一种将数据存储在应用程序的内存中&#xff0c;以加速数据访问的技术。缓存的数据可以是频繁访问的数据&#xff0c;以减少对慢速数据源&#xff08;如数据库或网络&#xff09;的访问。缓存通常有一些缓存过期淘汰策略&#xff0c;以确保缓存中的数据保持最新和有效…...

激光焊接汽车尼龙塑料配件透光率测试仪

激光塑性成型技术是近年来塑性加工界出现的一种新技术。通常塑料主要是通过加热加压依赖模具成型。这对于单品种、大批量生产是有效的&#xff1b;而对于各种不同形状的塑料制件则需要昂贵的模具‚装置也较庞大。 高度聚焦的激光束垂直照射在待变形的板料上‚由于塑料直接吸收激…...

2023年高校大数据实验室建设方案

大数据实验室建设方案具体内容包括&#xff1a;人才培养方案建设、课程资源建设、师资建设、实验室建设、教学服务建设。 泰迪打造国内领先的大数据人工智能及课程资源&#xff0c;包括&#xff1a;商务数据分析实训管理平台、云计算资源管理平台、大数据编程实训平台、商务数据…...

计网第五章(运输层)(一)

在前面的博客中&#xff0c;总是说主机之间进行通信。但实际上通信的真正的实体是位于通信两端主机中的进程。 一、运输层基本概述 运输层的任务就是为运行在不同主机上的应用进程提供直接的通信服务&#xff0c;运输层的协议又称为端到端协议。运输层中使用不同的端口来对应…...

ILS解析漏洞复现

搭建好ILS后&#xff0c;访问127.0.0.1:8000 写一个phpinfo的脚本 可以看到。现在是不能访问的 赋予 IIS 解析 phpinfo 能力 打开服务器管理器&#xff0c;打开 IIS 管理器 点击处理程序映射 再次访问&#xff0c;发现程序可以访问 将index.php改为index.png 此时php脚本自然是…...

0067__Git学习(1.本地仓库与暂存区)

Git学习&#xff08;1.本地仓库与暂存区&#xff09;_git暂存区_听枫1122的博客-CSDN博客 git的本地仓库在哪里_git本地仓库在哪里_二十英里法则的博客-CSDN博客...

Mac端交互式原型设计 Axure RP 8 for Mac汉化

Axure RP 8是一款专业的交互原型设计工具&#xff0c;它被广泛应用于用户体验设计、界面设计和产品原型制作等领域。该软件提供了丰富的功能和工具&#xff0c;使用户能够创建出具有高度交互性和可视化效果的原型。 Axure RP 8的主要特点和功能包括&#xff1a; 1. 快速原型&a…...

GO语言网络编程(并发编程)Sync

GO语言网络编程&#xff08;并发编程&#xff09;Sync 1、Sync 1.1.1. sync.WaitGroup 在代码中生硬的使用time.Sleep肯定是不合适的&#xff0c;Go语言中可以使用sync.WaitGroup来实现并发任务的同步。 sync.WaitGroup有以下几个方法&#xff1a; 方法名 功能 (wg * WaitG…...

如何在 Ubuntu 上安装 Nagios?

Nagios 的功能 Nagios 的一些关键功能包括&#xff1a; 主机和服务监控&#xff1a; Nagios 允许您使用提供实时状态数据的插件来监控主机&#xff08;可以是物理机或虚拟机&#xff09;以及 HTTP、SSH 和 SMTP 等服务。此功能使您能够全面了解整个基础设施的运行状况和可用性…...

汽车技术发展趋势及我国节能与新能源汽车技术

一、世界汽车技术发展趋势 汽车技术正向着低碳化、信息化、智能化方向发展&#xff1b;“三化”趋势成为世界主要汽车强国、主要车企共同的战略选择。 主要汽车战略及方向 在“三化”趋势下&#xff0c;各汽车强国在汽车节能技术、新能源汽车技术、智能网联汽车技术等方面持续…...

如何实现负载均衡

在如今互联网应用日益火热的背景下&#xff0c;为了保证应用程序的高可用性和高性能&#xff0c;负载均衡变得越来越重要。负载均衡是指将传入的请求分配到多个服务器上&#xff0c;以避免单一服务器的过载&#xff0c;提高系统的可用性和性能。而PHP作为一种广泛使用的服务器端…...

Jetsonnano B01 笔记3:GPIO上拉下拉-输入输出读取

今日继续我的jetsonnano学习之路&#xff0c;今日学习的是GPIO的上拉下拉&#xff0c;输入输出的读取&#xff0c;文章贴出完整操作步骤过程&#xff0c;贴出源码。 目录 Linux常用文件命令&#xff1a; ls&#xff08;list&#xff09;列表&#xff1a; man&#xff1a; …...

COMO-ViT论文阅读笔记

Low-Light Image Enhancement with Illumination-Aware Gamma Correction and Complete Image Modelling Network 这是一篇美团、旷视、深先院、华为诺亚方舟实验室、中国电子科技大学 五个单位合作的ICCV2023的暗图增强论文&#xff0c;不过没有开源代码。 文章的贡献点一个是…...

智慧燃气:智慧燃气发展的讨论

关键词&#xff1a;智慧燃气、智能管网、智慧燃气系统、智能燃气、智慧燃气建设、智慧燃气平台 智慧燃气是什么&#xff1f; 智慧燃气是以智能管网建设为基础&#xff0c;利用先进的通信、传感、储能、微电子、数据优化管理和智能控制等技术&#xff0c;实现天然气与其他能源…...

音视频会议需要哪些设备配置

音视频会议需要哪些设备配置&#xff1f;音视频会议需要&#xff1a;视频会议摄像头、麦克风、扬声器、显示设备、网络连接设备、视频会议服务器、视频会议软件等。 1. 视频会议摄像头&#xff1a;用于捕捉与传输视频图像&#xff0c;可以选择高清摄像头&#xff0c;提供更出色…...

性能测试 —— Jmeter事务控制器

事务&#xff1a; 性能测试中&#xff0c;事务指的是从端到端&#xff0c;一个完整的操作过程&#xff0c;比如一次登录、一次 筛选条件查询&#xff0c;一次支付等&#xff1b;技术上讲&#xff1a;事务就是由1个或多个请求组成的 事务控制器 事务控制器类似简单控制器&…...

【Tomcat7部署Springboot版本不兼容问题】

Tomcat7部署Springboot版本不兼容 报错网上解决方案tomcat7可以部署springboot哪些版本分析原因解决方法 报错 SEVERE: Unable to deploy collapsed ear in war StandardEngine[Catalina].StandardHost[localhost].StandardContext[/demo] org.apache.openejb.OpenEJBException…...

RabbitMQ消息中间件

RabbitMQ消息中间件 RabbitMQ简介windows下安装RabbitMQRabbitMQ基本概念RabbitMQ简单模式RabbitMQ工作队列模式RabbitMQ发布订阅模式RabbitMQ路由模式RabbitMQ主题模式RabbitMQ RPC模式RabbitMQ发布确认模式...

UNIAPP之js/nvue混淆探索

因项目需要对UNIAPP的js混淆做了一些调研 混淆教程: https://uniapp.dcloud.net.cn/tutorial/app-sec-confusion.html 按照教程配置进行打包正式包进行混淆 下载正式包将 .ipa改为.zip 解压获取到HBuilder.app 右键显示包内容 获取到混淆的key 不同时间进行打包混淆同一文…...

Excel文件生成与下载(SpringBoot项目)(easypoi)

说明 通过接口&#xff0c;导出表格。 使用SpringBoot框架和easypoi表格解析框架&#xff0c;生成Excel表格&#xff0c;并通过接口下载。 表格示例 依赖 版本 <easypoi.version>4.4.0</easypoi.version>依赖 <!-- easypoi --> <dependency><…...

社群团购对接,【概率思维】可以增加你做项目的成功率!

社群团购对接&#xff0c;【概率思维】可以增加你做项目的成功率&#xff01; 今天来聊一个关于概率的问题&#xff0c;我们不管去做社群团购项目、做流量&#xff0c;还是做销售&#xff0c;我们都要有概率思维&#xff0c;有了这个思维以后&#xff0c;就可以增加你的成功率…...

不同场景下的JMETER设置

不同场景下的JMETER设置 1.基准测试 验证主要业务在单用户运行下的性能指标&#xff0c;为多用户并发并发和混合场景的性能分析提供基础参考。 基准测试JMETER线程组设置(在1秒内执行5个线程循环一次)&#xff1a; 2.并发测试 多用户在同一时间访问某一个模块或则应用的场景&…...

新手请进,Python是什么,Python简介!

Python 是荷兰人 Guido van Rossum &#xff08;吉多范罗苏姆&#xff0c;中国程序员称其为“龟叔”&#xff09;在 1990 年初开发的一种解释型编程语言。 图1&#xff1a;Python 的标志&#xff08;Logo&#xff09; Python 的诞生是极具戏曲性的&#xff0c;据 Guido 自述记载…...

《Python魔法大冒险》005 魔法挑战:自我介绍机器人

魔法师和小鱼坐在图书馆的一扇窗户旁&#xff0c;窗外的星空闪烁着神秘的光芒。魔法师轻轻地拍了拍小鱼的肩膀。 魔法师&#xff1a; 小鱼&#xff0c;你已经学会了编写简单的魔法程序&#xff0c;现在我要教你如何创造一个有自己思想的机器人&#xff0c;让它能够和我们一样&…...

常见的网络欺诈风险类型有哪些?

身份伪冒&#xff0c;这是非常典型的第三方欺诈&#xff0c;指的是不法分子使用虚假身份证等身份信息、未经他人同意而冒用他人身份获取贷款的骗贷行为。 另外还有帐号垃圾注册&#xff0c;通过大规模的帐号注册&#xff0c;养号养卡&#xff0c;控制帐号骗贷。此外还有中介包装…...

GE IS220PAICH2A 336A4940CSP11 数字量输入模块产品应用领域

GE IS220PAICH2A 336A4940CSP11 是一款数字量输入模块&#xff0c;通常用于工业自动化和控制系统中&#xff0c;用于监测和采集数字输入信号。这种类型的模块可以在各种应用领域中发挥作用&#xff0c;以下是一些可能的应用领域&#xff1a; 工业过程控制&#xff1a; GE IS220…...

element el-table 设置fixed导致行错乱问题

首先看有问题的样式&#xff1a; 解决&#xff1a; // 解决左右 对不齐 的情况 // el-table 左右有列固定时&#xff0c;fixed为left和right时&#xff0c;行未对齐解决办法 // * 产生原因&#xff1a; el-table底部有滚动条&#xff0c;固定列底部没有滚动条 // * 解决办法&…...

新产品开发的5个步骤/优化网站技术

Google Maps API简易教程&#xff08;二&#xff09; 2013-03-04 14:24 williamcai 博客园 我要评论(0) 字号&#xff1a;T | T单击一下Marker&#xff0c;就会触发click事件。拖曳当前地图&#xff0c;将会触发center_changed事件。而google.maps.addListener监听地图上每一个…...

做二手车放在哪个网站好/搜索推广出价多少合适

Dynamic Network Embedding by Modeling Triadic Closure Process 作者是Lekui Zhou, Yang Yang, Xiang Ren, Fei Wu, Yueting Zhuang&#xff0c;发表于AAAI2018。主要是利用三元闭包过程建模动态网络嵌入&#xff0c;并且生成节点的embedding用于下游任务。 1 背景知识 网…...

广州易网网站建设/白嫖永久服务器

sk_buff是内核网络子系统的一个支柱结构&#xff0c;各层协议都依赖于sk_buff而存在&#xff0c;所以了解它的设计是很重要的&#xff0c;这其中当然也包括了很重要的网络缓冲区的管理机制&#xff0c;下面一个系列都围绕它来展开。 sk_buff作为一个buffer&#xff0c;最重要的…...

给自己的网站做软件测试 步骤/优化服务内容

正题第四题&#xff1a;[SDOI2009]Elaxia的路线这道题好像很麻烦。。。首先我们可以知道&#xff0c;如果边(x,y,c)为x1到y1最短路路径上的一条边&#xff0c;那么它肯定满足d[x1][x]d[y][y1]cd[x][y] || d[x1][y]d[x][y1]cd[x][y]那么知道了这个东西&#xff0c;我…...

朔州网站建设价格/小程序开发软件

本文转载自&#xff1a;http://www.blogjava.net/BlueDavy/archive/2009/04/28/267970.html&#xff0c; 转载请注明 在这篇blog中放置了我收集的一些网站架构相关的PPT和文章&#xff0c;提供给大家下载&#xff0c;如果大家有相关的好的PPT、文章的话&#xff0c;也欢迎推…...

服务网站开发/信息流优化师

项目介绍 随着我国大学生数量的不断增加&#xff0c;如果能够让大学生更好的养成对应的消费习惯&#xff0c;并且能够自控的管理好自己的收支情况&#xff0c;是很多教育工作者研究的一个主要问题之一&#xff0c;本系统的开发主要是为了在一定程度上帮助大学生养成良好的收支…...