移植VL53L1X(一)GD32F470移植VL53L1X

        好不容易搞定了VL53L1X的移植,结果立创那边告诉我它的移植已经有人做了,气死我了。看了看CSDN上似乎还没有多少人细讲VL53L1X的移植,之前我也踩了很长一段时间的坑,那就在CSDN上发一篇文档吧。

一、模块简述

        VL53L1X是由ST推出的新一代TOF测距模块,属于VL53L0X的升级版本,其测距距离和测量频率由VL53L0X的1.2m/50Hz或2.0m/33Hz提升至2.0m/100Hz或4.0m/50Hz,且保留了VL53L0X小体积和廉价的优势,其引脚也完全兼容VL53L0X(注意,仅仅是引脚兼容)。

二、驱动介绍

        VL53L1X通过IIC总线与主机进行通讯,ST官方并未公开VL53L1X的寄存器,而是提供了一整套函数库,以实现对VL53L1X的配置/读取,用户仅需实现最底层的一些操作,例如毫米级延时、IIC发送接收等。这个库有Full和ULD(ultra lite driver)两个版本,编号分别为IMG007和IMG009,如下图所示:

Full版本(IMG007)是VL53L1X驱动的完全体版本,其可以使用VL53L1X的所有功能,且拥有最高100Hz的测距频率,但也正因如此,其底层操作多且复杂,包含IIC读写、引脚操作、引脚中断、延时等,占用flash较大,移植难度也相对较高。为解决上述问题,ST推出了简化后的ULD版本驱动(IMG009),其大幅精简了文件数量,占用flash空间由9KB下降至2.3KB,且移植时仅需实现IIC读写与延时即可,移植难度低了许多,代价是VL53L1X的GPIO相关功能无法使用,且最高测距频率仅支持66Hz。本次移植采用ULD版本驱动。

三、驱动移植

硬件:正点原子ATK-MS53L1M模块(工作于IIC模式,等效于一块VL53L1X传感器)

           梁山派GD32F470开发板

软件:MDK  v5.18

接线:模块                开发板

           VCC ------------ 5V

           GND ----------- GND

            SCL ------------ PB8

           SDA ------------- PB7

1. 下载驱动

        首先前往ST官网下载DataSheet和ULD版本驱动,完成后打开驱动文件夹,子文件夹如下所示:

其中API文件夹是驱动所在位置,Example文件夹则是ST基于X-NUCLEO-53L1A1评估板实现的移植示例,包含MDK、IAR、cubeIDE等多个版本,README提供了关于本库文件的一些信息。打开API文件夹,可以发现core和platform两个文件夹,这便是驱动库的本体了,其中core文件夹提供了驱动VL53L1X所需的API,而platform文件夹中便是驱动库的底层操作了,其中的platform.h文件定义了需要实现的底层操作函数,在platform.c中实现即可完成移植。

2. 建立工程

        在MDK中建立工程,并将core和platform中的文件包含进工程中,如下所示:

3. 函数实现

        platform.c中需要实现的函数包括延时和IIC读写两类,数量不多,接下来讲解如何实现。

3.1 延时函数

        与完整版驱动需要提供微秒级和毫秒级两种延时不同,此处仅需提供毫秒级延时函数即可,因此其实没什么好说的,直接将systick.c中已有的延时函数拷贝进去即可,代码如下:

int8_t VL53L1_WaitMs(uint16_t dev, int32_t wait_ms){
	int8_t status = 0;
	
	delay_1ms((uint32_t)wait_ms);
	
	return status; // to be implemented
}

3.2 IIC读写

3.2.1 IIC介绍

        IIC(Inter-Integrated Circuit),中文名集成电路总线,它是一种串行通信总线,使用多主从架构,由飞利浦公司在1980年代为了让高速主机用以连接低速周边设备而发展。其硬件特点为一般仅有两根线,分别传输时钟信号(SCL)和数据(SDA),其拥有3种传输模式:标准模式(100Kb/s)、快速模式(400Kb/s)和高速模式(3.4Mb/s)。

        数据传输

        IIC为同步通讯接口,其帧格式为MSB优先,时钟高电平与数据位一一对应,一个高电平时钟对应一个数据位,只有在SCL为低电平期间,才允许 SDA上的电平改变状态。在发送完8位(1字节)数据后需要接收方发送ACK信号进行应答,在读操作时,如果主机停止读取数据,由主机发送给从机非应答信号NACK。

        传输起始与结束

        在SCL保持高电平时拉低SDA,表示通讯开始,在SDA保持低电平时拉高SCL,表示通讯结束。

        设备地址

        在进行读写之前,需要先发送设备地址来呼叫对应设备,IIC设备拥有7位设备地址+1位读写标志位,0为写1为读,帧格式如下:

可以理解为一个设备拥有2个地址,一个为读地址一个为写地址。

3.2.2 代码实现

        首先打开VL53L1X的Datasheet,跳转至第四章"Control interface",其中详细讲述了VL53L1X的读写时序与步骤,其中最关键的是这两张图:

以上为VL53L1X的连续读/写数据帧图,由上可见, 在连续写入时,仅需向传感器传输写入的起始寄存器地址,然后写入数据即可,传感器会自动进行地址递增。在连续读取时则需先发起一次写入通讯,向寄存器中写入需要读取的寄存器地址,结束本次通讯后立刻再发起一次读取通讯,由传感器持续向主机传输数据,但值得注意的是,连续读取示意图最后一部分的示意是错误的,主机在终止通讯之前必须先停止发送应答位以停止从机的数据发送,如下所示:

具体的连续读/写代码实现如下所示:

/* 
* 功能	将提供的字节缓冲区写入设备,用于对接platform.c中相关函数
* 参数	dev_addr:	7位设备地址
*		reg_addr:	16位寄存器地址
*		*pdata:	数据缓冲区指针
*		len:		数据长度
* 返回	操作是否成功
*/
int8_t vl53_writeBytes(uint8_t dev_addr, uint16_t reg_addr, uint8_t *pdata, uint32_t len)
{
	uint32_t i = 0;
	uint8_t *ptr = pdata;
	
	i2c_start_on_bus(VL53_IIC);/* 生成起始信号 */
	while(RESET == i2c_flag_get(VL53_IIC, I2C_FLAG_SBSEND));/* 等待SBSEND标志位置位 */
	
	i2c_master_addressing(VL53_IIC, dev_addr, I2C_TRANSMITTER);/* 发送地址与写指令 */
	while(RESET == i2c_flag_get(VL53_IIC, I2C_FLAG_ADDSEND));/* 等待ADDSEND置位 */	
	i2c_flag_clear(VL53_IIC, I2C_FLAG_ADDSEND);/* 清除ADDSEND标志位 */
	
	while(RESET == i2c_flag_get(VL53_IIC, I2C_FLAG_TBE));/* 等待发送缓冲区清零 */
	
	i2c_data_transmit(VL53_IIC, (uint8_t)((reg_addr & 0xff00) >> 8));/* 发送高8位寄存器地址 */
	while(RESET == i2c_flag_get(VL53_IIC, I2C_FLAG_TBE));/* 等待发送缓冲区清零 */
	
	i2c_data_transmit(VL53_IIC, (uint8_t)(reg_addr & 0x00ff));/* 发送低8位寄存器地址 */
	while(RESET == i2c_flag_get(VL53_IIC, I2C_FLAG_TBE));/* 等待发送缓冲区清零 */
	
	while(i < len)/* 循环发送需要写入的数据 */
	{
		i2c_data_transmit(VL53_IIC, *ptr);/* 发送数据缓冲区指针指向的数据 */
		while(RESET == i2c_flag_get(VL53_IIC, I2C_FLAG_TBE));/* 等待发送缓冲区清零 */
		ptr++;
		i++;
	}
	while(RESET == i2c_flag_get(VL53_IIC, I2C_FLAG_BTC));/* 等待最后一个字节发送完成 */
	i2c_stop_on_bus(VL53_IIC);/* 生成停止信号 */
	
	return 0;
}

/*
* 功能	从设备读取请求的字节数,用于对接platform.c中相关函数
* 参数	dev_addr:	7位设备地址
*		reg_addr:	16位寄存器地址
*		*pdata:	数据缓冲区指针,用于存储读出的数据
*		len:		数据长度	
* 返回	操作是否成功
*/
int8_t vl53_readBytes(uint8_t dev_addr, uint16_t reg_addr, uint8_t *pdata, uint32_t len)
{
	uint32_t i = 0;
	uint8_t *ptr = pdata;
	
	i2c_start_on_bus(VL53_IIC);/* 生成起始信号 */
	while(RESET == i2c_flag_get(VL53_IIC, I2C_FLAG_SBSEND));/* 等待SBSEND标志位置位 */
	
	i2c_master_addressing(VL53_IIC, dev_addr, I2C_TRANSMITTER);/* 发送地址与写指令 */
	while(RESET == i2c_flag_get(VL53_IIC, I2C_FLAG_ADDSEND));/* 等待ADDSEND置位 */	
	i2c_flag_clear(VL53_IIC, I2C_FLAG_ADDSEND);/* 清除ADDSEND标志位 */	
	
	while(RESET == i2c_flag_get(VL53_IIC, I2C_FLAG_TBE));/* 等待发送缓冲区清零 */
	
	i2c_data_transmit(VL53_IIC, (uint8_t)((reg_addr & 0xff00) >> 8));/* 发送高8位寄存器地址 */
	while(RESET == i2c_flag_get(VL53_IIC, I2C_FLAG_TBE));/* 等待发送缓冲区清零 */
	
	i2c_data_transmit(VL53_IIC, (uint8_t)(reg_addr & 0x00ff));/* 发送低8位寄存器地址 */
	while(RESET == i2c_flag_get(VL53_IIC, I2C_FLAG_TBE));/* 等待发送缓冲区清零 */
	
	while(RESET == i2c_flag_get(VL53_IIC, I2C_FLAG_BTC));/* 等待最后一个字节发送完成 */
	i2c_stop_on_bus(VL53_IIC);/* 生成停止信号 */
	
	i2c_start_on_bus(VL53_IIC);/* 再次生成起始信号 */
	while(RESET == i2c_flag_get(VL53_IIC, I2C_FLAG_SBSEND));/* 等待SBSEND标志位置位 */
	
	i2c_master_addressing(VL53_IIC, dev_addr, I2C_RECEIVER);/* 发送地址与读指令 */
	if(SET == i2c_flag_get(VL53_IIC, I2C_FLAG_RBNE))
		i2c_data_receive(VL53_IIC);/* 读取接收缓冲区的数据以清空接收缓冲区 */	
	while(RESET == i2c_flag_get(VL53_IIC, I2C_FLAG_ADDSEND));/* 等待ADDSEND置位 */
	i2c_flag_clear(VL53_IIC, I2C_FLAG_ADDSEND);/* 清除ADDSEND标志位 */		
	
	while(i < len)
	{
		if(i == len - 1)
			i2c_ack_config(VL53_IIC, I2C_ACK_DISABLE);/* 在接收最后一个数据前失能应答发送功能 */
		
		while(RESET == i2c_flag_get(VL53_IIC, I2C_FLAG_RBNE));/* 等待接收缓冲区收到数据 */
		*ptr = i2c_data_receive(VL53_IIC);/* 读取接收缓冲区的数据 */
		
		ptr++;
		i++;
	}
	i2c_stop_on_bus(VL53_IIC);/* 生成停止信号 */
	i2c_ack_config(VL53_IIC, I2C_ACK_ENABLE);/* 在接收完成后重新使能应答发送功能 */

	return 0;
}

其与platform.c中的函数对接如下:

int8_t VL53L1_WriteMulti( uint16_t dev, uint16_t index, uint8_t *pdata, uint32_t count) {
	int8_t status;
	
	status = vl53_writeBytes((uint8_t)dev, index, pdata, count);
	
	return status; // to be implemented
}

int8_t VL53L1_ReadMulti(uint16_t dev, uint16_t index, uint8_t *pdata, uint32_t count){
	int8_t status;
	
	status = vl53_readBytes((uint8_t)dev, index, pdata, count);
	
	return status; // to be implemented
}

int8_t VL53L1_WrByte(uint16_t dev, uint16_t index, uint8_t data) {
	int8_t status;
	
	status = vl53_writeBytes((uint8_t)dev, index, &data, 1);
	
	return status; // to be implemented
}

int8_t VL53L1_WrWord(uint16_t dev, uint16_t index, uint16_t data) {
	int8_t status;
	uint8_t buffer[2];
	
	// Split 16-bit word into MS and LS uint8_t
	buffer[0] = (uint8_t)(data >> 8);
	buffer[1] = (uint8_t)(data &  0x00FF);
	
	status = vl53_writeBytes((uint8_t)dev, index, buffer, 2);
	
	return status; // to be implemented
}

int8_t VL53L1_WrDWord(uint16_t dev, uint16_t index, uint32_t data) {
	int8_t status;
	uint8_t  buffer[4];

	// Split 32-bit word into MS ... LS bytes
	buffer[0] = (uint8_t) (data >> 24);
	buffer[1] = (uint8_t)((data &  0x00FF0000) >> 16);
	buffer[2] = (uint8_t)((data &  0x0000FF00) >> 8);
	buffer[3] = (uint8_t) (data &  0x000000FF);
	
	status = vl53_writeBytes((uint8_t)dev, index, buffer, 4);
	
	return status; // to be implemented
}

int8_t VL53L1_RdByte(uint16_t dev, uint16_t index, uint8_t *data) {
	int8_t status;
	
	status = vl53_readBytes((uint8_t)dev, index, data, 1);
	
	return status; // to be implemented
}

int8_t VL53L1_RdWord(uint16_t dev, uint16_t index, uint16_t *data) {
	int8_t status;
	uint8_t buffer[2];
	
	status = vl53_readBytes((uint8_t)dev, index, buffer, 2);
	
	*data = (uint16_t)(((uint16_t)(buffer[0])<<8) + (uint16_t)buffer[1]);
	
	return status; // to be implemented
}

int8_t VL53L1_RdDWord(uint16_t dev, uint16_t index, uint32_t *data) {
	int8_t status;
	uint8_t buffer[4];
	
	status = vl53_readBytes((uint8_t)dev, index, buffer, 4);
	
	*data = ((uint32_t)buffer[0]<<24) + ((uint32_t)buffer[1]<<16) + ((uint32_t)buffer[2]<<8) + (uint32_t)buffer[3];
	
	return status; // to be implemented
}

至此,VL53L1X的驱动移植完成,接下来根据API手册调用API进行初始化即可完成传感器初始化与读取。完整代码将会在下一篇文章中开源。

  • 3
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
[UV2] ORGANIZATION="Microsoft" NAME="Microsoft", "ALIENTEK" EMAIL="277038235@qq.com" ARMSEL=1 BOOK0=UV3\RELEASE_NOTES.HTM("uVision Release Notes",GEN) [ARM] PATH="F:\\STM32MDK\\ARM\\" VERSION=3.80a PATH1="C:\Program Files\CodeSourcery\Sourcery G++ Lite\" CPUDLL0=SARM.DLL(TDRV0,TDRV5,TDRV6,TDRV8) # Drivers for ARM7/9 devices CPUDLL1=SARMCM3.DLL(TDRV1,TDRV3,TDRV4,TDRV5,TDRV7,TDRV8) # Drivers for Cortex-M devices BOOK0=HLP\RELEASE_NOTES.HTM("Release Notes",GEN) BOOK1=HLP\ARMTOOLS.chm("Complete User's Guide Selection",C) BOOK2=HLP\RL_RELEASE_NOTES.HTM("RTL-ARM Release Notes",GEN) BOOK3=HLP\RVI.chm("RV Compiler Introduction",GEN) BOOK4=C:\Program Files\CodeSourcery\Sourcery G++ Lite\share\doc\arm-2007q3-53-arm-none-eabi\pdf\gcc\gcc.pdf("GNU C Compiler",GEN) BOOK5=C:\Program Files\CodeSourcery\Sourcery G++ Lite\share\doc\arm-2007q3-53-arm-none-eabi\pdf\as.pdf("GNU Assembler",GEN) BOOK6=C:\Program Files\CodeSourcery\Sourcery G++ Lite\share\doc\arm-2007q3-53-arm-none-eabi\pdf\ld.pdf("GNU Linker",GEN) BOOK7=C:\Program Files\CodeSourcery\Sourcery G++ Lite\share\doc\arm-2007q3-53-arm-none-eabi\pdf\binutils.pdf("GNU Binary Utilities",GEN) TDRV0=BIN\UL2ARM.DLL("ULINK ARM Debugger") TDRV1=BIN\UL2CM3.DLL("ULINK Cortex Debugger") TDRV2=BIN\AGDIRDI.DLL("RDI Interface Driver") TDRV3=BIN\ABLSTCM.dll("Altera Blaster Cortex Debugger") TDRV4=BIN\lmidk-agdi.dll("Luminary Eval Board") TDRV5=Signum\SigUV3Arm.dll("Signum Systems JTAGjet") TDRV6=Segger\JLTAgdi.dll("J-LINK / J-TRACE") TDRV7=Segger\JL2CM3.dll("Cortex-M3 J-LINK") TDRV8=STLink\ST-LINKIII-KEIL.dll ("ST-Link Debugger") LIC0=EXJEV-PGITZ-RXIUD-8A562-3JTCY-2C6VU [ARMADS] PATH="F:\\STM32MDK\\ARM\\" PATH1="BIN40\"
gd32f470是一款高性能的微控制器,而emwin是一个功能强大的嵌入式图形界面软件。要在gd32f470移植emwin,首先需要了解gd32f470的硬件特性和emwin的软件要求。 gd32f470具有丰富的硬件资源,包括高性能的ARM Cortex-M4内核、大容量的Flash存储器和SRAM、多个通信接口等。emwin则需要至少16位的处理器和足够的存储空间来运行。 首先,我们需要下载emwin软件包,并按照官方提供的移植指南进行相应的设置。在移植过程中,需要根据gd32f470的硬件资源和emwin的软件要求进行适配。例如,配置gd32f470的时钟源和时钟频率,为emwin提供足够的运行速度。 然后,根据gd32f470的外设资源和emwin的功能需求,进行相关的引脚配置和中断配置。如需使用触摸屏功能,需要配置相应的引脚和中断。同时,根据实际需求,可能还需要对外设进行初始化和设置。 接下来,在项目中包含emwin的源代码文件,并根据gd32f470的编译器和开发环境进行相关的编译和链接设置。确保编译器正确地识别emwin的源代码,并生成适合gd32f470的可执行文件。 最后,在应用程序中调用emwin的相关函数,来实现图形界面的绘制和控制。可以根据需要,编写应用程序来显示图像、文本、按钮等元素,并对其进行交互。 移植过程中,需要注意解决可能出现的硬件兼容性和软件冲突问题。通过逐步调试和测试,可以确保gd32f470上的emwin移植成功,并实现相应的图形界面功能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值