RK3568笔记四十一:DHT11驱动开发测试

若该文为原创文章,转载请注明原文出处。

记录开发单总线,读取DHT11温湿度

一、DHT11介绍

DHT11是串行接口(单线双向)DATA 用于微处理器与 DHT11之间的通讯和同步,采用单总线数据格式,一次通讯时间4ms左右,数据分小数部分和整数部分。

1、协议及数据格式

DHT11 采用单总线协议与单片机通信,单片机发送一次复位信号后,DHT11 从低功耗模式转换到高速模式,等待主机复位结束后,DHT11 发送响应信号,并拉高总线准备传输数据。一次完整的数据为 40bit,按照高位在前,低位在后的顺序传输。

数据格式为:8bit 湿度整数数据+8bit 湿度小数数据+8bit 温度整数数据+8bit 温度小数数据+8bit 校验和,一共 5 字节(40bit)数据。由于 DHT11 分辨率只能精确到个位,所以小数部分是数据全为 0。校验和为前 4 个字节数据相加,校验的目的是为了保证数据传输的准确性。
 

2、时序

根据时序读取DHT11流程为:

1、主机发送复位信号

2、等待DHT11 发送响应信号

3、读取数据(40bit,5个字节)

前面4字节为数据位,第5字节为校验和位

二、原理图

使用的是前面的IO口,GPIO3_PC4。IO口可以跟换,设备的节点需要对应修改。

三、创建节点

1、在设备树中创建设备节点

修改/home/alientek/rk3568_linux_sdk/kernel/arch/arm64/boot/dts/rockchip/目录下的rk3568-atk-evb1-ddr4-v10.dtsi文件,添加gpiodht11节点

gpiodht11 {
  compatible = "alientek,dht11";
  pinctrl-names = "alientek,dht11";
  pinctrl-0 = <&dht11_gpio>;
  dht11-gpio = <&gpio3 RK_PC4 GPIO_ACTIVE_HIGH>;
  status = "okay";
};

2、创建设备的 pinctrl 节点

dht11-gpios{
  /omit-if-no-ref/
  dht11_gpio: dht11-pin {
  rockchip,pins =
    <3 RK_PC4 RK_FUNC_GPIO &pcfg_pull_none>;
  };
};

修改后,重新编译内核,生成boot.img文件,重新烧写即可。

重启后会在/proc/device-tree下找到gpiodht11节点.

四、驱动编写

1、drv_dht11.c

/* 
dht11-gpios{
  /omit-if-no-ref/
  dht11_gpio: dht11-pin {
  rockchip,pins =
    <3 RK_PC4 RK_FUNC_GPIO &pcfg_pull_none>;
  };
};

gpiodht11 {
  compatible = "alientek,dht11";
  pinctrl-names = "alientek,dht11";
  pinctrl-0 = <&dht11_gpio>;
  dht11-gpio = <&gpio3 RK_PC4 GPIO_ACTIVE_HIGH>;
  status = "okay";
};
*/


#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of_gpio.h>
#include <linux/semaphore.h>
#include <linux/timer.h>
#include <linux/irq.h>
#include <linux/wait.h>
#include <linux/poll.h>
#include <linux/fs.h>
#include <linux/fcntl.h>
#include <linux/platform_device.h>
//#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>


#define DHT11DEV_CNT           1              /* 设备号长度 */
#define DHT11DEV_NAME          "dtsplatdht11"   /* 设备名字 */


/* dht11dev 设备结构体 */
struct dht11dev_dev{
  dev_t devid;                  /* 设备号 */
  struct cdev cdev;             /* cdev */
  struct class *class;          /* 类 */
  struct device *device;        /* 设备 */ 
  struct device_node *node;     /* dht11dev 设备节点 */
  int gpio_dht11;                 /* dht11dev 灯 GPIO 标号 */
};

struct dht11dev_dev dht11dev; /* dht11d ev 设备 */

unsigned char getdata[4] = {0xff, 0xff, 0xff, 0xff};

static void dht11_release( void )
{
    gpio_direction_output(dht11dev.gpio_dht11, 1);
}

static void dht11_start(void)
{
    gpio_direction_output(dht11dev.gpio_dht11, 1);
    mdelay(30);
    
    gpio_set_value( dht11dev.gpio_dht11, 0);
    mdelay(20);
    
    gpio_set_value(dht11dev.gpio_dht11, 1);
    udelay(40);
    
    gpio_direction_input(dht11dev.gpio_dht11);
}


static int dht11_wait_ack(void)
{
    long timeout_us = 50000;
 
    /* 等待低电平 */
    while (gpio_get_value(dht11dev.gpio_dht11) && --timeout_us)
    {
        udelay(1);
    }
    if (!timeout_us)
    {
        return -1;
    }
 
    /* 现在是低电平 */
    /* 等待高电平 */
    timeout_us = 50000;
    
    while (!gpio_get_value(dht11dev.gpio_dht11) && --timeout_us)
    {
        udelay(1);
    }
    
    if (!timeout_us)
    {
        return -1;
    }
 
    /* 现在是高电平 */
    /* 等待低电平 */
    timeout_us = 20000;
    while (gpio_get_value(dht11dev.gpio_dht11) && --timeout_us)
    {
        udelay(1);
    }
    
    if (!timeout_us)
    {
        return -1;
    }

    
    return 0;
}
 
 
static int dht11_read_byte( unsigned char datalist )
{
    int i;
    unsigned char data = 0;
    int timeout_us = 20000;
    u64 pre = 0, last = 0;
    
    for (i = 0; i < 8; i++)
    {
        /* 现在是低电平 */
        /* 等待高电平 */
        timeout_us = 20000;
        while (!gpio_get_value(dht11dev.gpio_dht11) && --timeout_us)
        {
            udelay(1);
        }
        
        if (!timeout_us)
        {
            return -1;
        }
 
        /* 现在是高电平 */
        /* 等待低电平,累加高电平的时间 */
        timeout_us = 20000000;
 
        /* set another gpio low  */
        pre = ktime_get_boot_ns();
        while (1) 
        {
            last = ktime_get_boot_ns();
            if (last - pre >= 40000)
                break;
        }
 
        if (gpio_get_value(dht11dev.gpio_dht11))
        {
            /* get bit 1 */
            data = (data << 1) | 1;
            /* 当前位的高电平未结束, 等待 */
            timeout_us = 20000;
            while (gpio_get_value(dht11dev.gpio_dht11) && --timeout_us)
            {
                udelay(1);
            }
            if (!timeout_us)
            {
                return -1;
            }
        }
        else
        {
            /* get bit 0 */
            data = (data << 1) | 0;
        }
    }
 
    getdata[datalist] = data;
    
    return 0;
}
 
 
static int dht11_get_value(void)
{
	unsigned long flags;
	int i;
	
	
	local_irq_save(flags);  // 关中断
	
	/* 1. 发送高脉冲启动DHT11 */
    dht11_start();
	
	/* 2. 等待DHT11就绪 */
    if (dht11_wait_ack())
    {
        local_irq_restore(flags); // 恢复中断
        return -EAGAIN;
    }
	
	/* 3. 读5字节数据 */
    for (i = 0; i < 5; i++)
    {
		dht11_read_byte(i); 
    }
	
    /* 4. 释放总线 */
    dht11_release();
	
	local_irq_restore(flags); // 恢复中断
	
	/* 5. 根据校验码验证数据 */
    if (getdata[4] != (getdata[0] + getdata[1] + getdata[2] + getdata[3]))
    {
		getdata[0] = 0xff;
		getdata[1] = 0xff;
		getdata[2] = 0xff;
		getdata[3] = 0xff;
		
        return -1;
    }	
    
    return 0;
}


static int dht11_gpio_init(struct device_node *nd)
{
  int ret;
  
  /* 从设备树中获取 GPIO */
  dht11dev.gpio_dht11 = of_get_named_gpio(nd, "dht11-gpio", 0);
  if(!gpio_is_valid(dht11dev.gpio_dht11)) {
    printk(KERN_ERR "dht11dev: Failed to get dht11-gpio\n");
    return -EINVAL;
  }
  
  /* 申请使用 GPIO */
  ret = gpio_request(dht11dev.gpio_dht11, "DHT11");
  if (ret) {
    printk(KERN_ERR "dht11: Failed to request dht11-gpio\n");
    return ret;
  }
  
  /* 将 GPIO 设置为输出模式并设置 GPIO 初始电平状态 */
  gpio_direction_output(dht11dev.gpio_dht11,0);

  printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
  
  return 0;
}

/*
* @description : 打开设备
* @param – inode : 传递给驱动的 inode
* @param - filp : 设备文件,file 结构体有个叫做 private_data 的成员变量
* 一般在 open 的时候将 private_data 指向设备结构体。
* @return : 0 成功;其他 失败
*/
static int dht11_open(struct inode *inode, struct file *filp)
{
  printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
  
  return 0;
}


/*
    linux driver 驱动接口: 
    实现对应的open/read/write等函数,填入file_operations结构体
*/
static ssize_t dht11_drv_read ( struct file *file, char __user *buf, 
                                size_t size, loff_t *offset)
{
    int ret =0;

    
    if( !dht11_get_value() )
	{
		//printk("data[0] = %d, data[1] = %d, data[2] = %d, data[3] = %d\n", getdata[0], getdata[1], getdata[2], getdata[3]);
		
        ret = copy_to_user(buf, getdata, sizeof(getdata));
		
        return ret;
    }
	
	return ret;

}
 


/*
* @description : 向设备写数据
* @param - filp : 设备文件,表示打开的文件描述符
* @param - buf : 要写给设备写入的数据
* @param - cnt : 要写入的数据长度
* @param - offt : 相对于文件首地址的偏移
* @return : 写入的字节数,如果为负值,表示写入失败
*/
static ssize_t dht11_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
  printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
  
  return 0;
}

static int dht11_drv_close(struct inode *node, struct file *file)
{
    printk(" %s line %d \r\n",  __FUNCTION__, __LINE__);
	
	dht11_get_value();
 
    return 0;
}


/* 设备操作函数 */
static struct file_operations dht11_fops = {
  .owner   = THIS_MODULE,
  .open    = dht11_open,
  .read    = dht11_drv_read,
  .write   = dht11_write,
  .release = dht11_drv_close,
};

/*
* @description : flatform 驱动的 probe 函数,当驱动与
* 设备匹配以后此函数就会执行
* @param - dev : platform 设备
* @return : 0,成功;其他负值,失败
*/
static int dht11_probe(struct platform_device *pdev)
{ 
  int ret;
  
  printk("dht11 driver and device was matched!\r\n");
  
  /* 初始化 DHT11 */
  ret = dht11_gpio_init(pdev->dev.of_node);
  if(ret < 0)
    return ret;
  
  /* 1、设置设备号 */
  ret = alloc_chrdev_region(&dht11dev.devid, 0, DHT11DEV_CNT, DHT11DEV_NAME);
  if(ret < 0) {
    pr_err("%s Couldn't alloc_chrdev_region, ret=%d\r\n", DHT11DEV_NAME, ret);
    goto free_gpio;
  }
  
  /* 2、初始化 cdev */
  dht11dev.cdev.owner = THIS_MODULE;
  cdev_init(&dht11dev.cdev, &dht11_fops);
  
  /* 3、添加一个 cdev */
  ret = cdev_add(&dht11dev.cdev, dht11dev.devid, DHT11DEV_CNT);
  if(ret < 0)
    goto del_unregister;
  
  /* 4、创建类 */
  dht11dev.class = class_create(THIS_MODULE, DHT11DEV_NAME);
  if (IS_ERR(dht11dev.class)) {
    goto del_cdev;
  }
  
  /* 5、创建设备 */
  dht11dev.device = device_create(dht11dev.class, NULL, dht11dev.devid, NULL, DHT11DEV_NAME);
  if (IS_ERR(dht11dev.device)) {
    goto destroy_class;
  }

  return 0;

destroy_class:
  class_destroy(dht11dev.class);
del_cdev:
  cdev_del(&dht11dev.cdev);
del_unregister:
  unregister_chrdev_region(dht11dev.devid, DHT11DEV_CNT);
free_gpio:
  gpio_free(dht11dev.gpio_dht11);
  
  return -EIO;
}

/*
* @description : platform 驱动的 remove 函数
* @param - dev : platform 设备
* @return : 0,成功;其他负值,失败
*/
static int dht11_remove(struct platform_device *dev)
{
  gpio_set_value(dht11dev.gpio_dht11, 0);/* 卸载驱动的时候 GPIO置0 */
  gpio_free(dht11dev.gpio_dht11); /* 注销 GPIO */
  cdev_del(&dht11dev.cdev); /* 删除 cdev */
  unregister_chrdev_region(dht11dev.devid, DHT11DEV_CNT);
  device_destroy(dht11dev.class, dht11dev.devid); /* 注销设备 */
  class_destroy(dht11dev.class); /* 注销类 */
  
  return 0;
}

/* 匹配列表 */
static const struct of_device_id dht11_of_match[] = {
{
  .compatible = "alientek,dht11" },
  { /* Sentinel */ }
};

MODULE_DEVICE_TABLE(of, dht11_of_match);

/* platform 驱动结构体 */
static struct platform_driver dht11_driver = {
    .driver = {
    .name = "rk3568-dht11", /* 驱动名字,用于和设备匹配 */
    .of_match_table = dht11_of_match, /* 设备树匹配表 */
  },
  
  .probe = dht11_probe,
  .remove = dht11_remove,
};

/*
* @description : 驱动模块加载函数
* @param : 无
* @return : 无
*/
static int __init dht11driver_init(void)
{
  return platform_driver_register(&dht11_driver);
}

/*
* @description : 驱动模块卸载函数
* @param : 无
* @return : 无
*/
static void __exit dht11driver_exit(void)
{
  platform_driver_unregister(&dht11_driver);
}

module_init(dht11driver_init);
module_exit(dht11driver_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("yifeng");
MODULE_INFO(intree, "Y");



程序是根据正点原子的注册platform平台驱动点亮LED修改,增加读功能. 这里有点需要注意,内存的使用,使用指针,不清楚为什么,会出错。

2、makefile

KERNELDIR := /home/alientek/rk3568_linux_sdk/kernel
ARCH=arm64
CROSS_COMPILE=/opt/atk-dlrk356x-toolchain/usr/bin/aarch64-buildroot-linux-gnu-

export  ARCH  CROSS_COMPILE

CURRENT_PATH := $(shell pwd)
obj-m := drv_dht11.o

build: kernel_modules

kernel_modules:
	$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules
clean:
	$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean

直接make编译,生成ko文件,拷贝到开发板。

五、应用程序编写

/***************************************************************
Copyright  2024-2029. All rights reserved.
文件名     : dht11App.c
作者       : yifeng
版本       : V1.0
描述       : 测试dth11驱动程序
其他       : 无
日志       : 初版V1.0 2024/07/18
***************************************************************/
#include <sys/types.h>
#include <sys/stat.h>
#include <linux/types.h>
#include <linux/i2c-dev.h>
#include <linux/i2c.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <stdlib.h>
#include <linux/fs.h>
#include <errno.h>
#include <assert.h>
#include <string.h>
#include <time.h>
 
#define DEV_FILE    "/dev/dtsplatdht11"
 
 
int main(void)
{
    int fd;
    int count_run = 0;
	int ret = 0;
    unsigned char data[4];
 
    fd = open(DEV_FILE, 0);
    if (fd == -1){
        printf("can not open file: %s \n", DEV_FILE);
        return -1;
    }
 
    while( count_run < 50000)
    {
        count_run++;

        if (read(fd, &data[0], 4) == 0) 
		{
            printf("get humidity  : %d.%d\n", data[0], data[1]);
            printf("get temprature: %d.%d\n", data[2], data[3]);
        } 
        else 
		{
            printf("read dht11 device fail!\n");
        }
		
        sleep(1);  // 1毫秒
    }
 
    close(fd);
 
    return 0;
}

程序比较简单,5秒读一次数据,并打印。

编译:

/opt/atk-dlrk356x-toolchain/bin/aarch64-buildroot-linux-gnu-gcc dht11App.c -o dht11App

六、测试

测试使用的是正点原子的ATK-DLRK3568板子

加载驱动

insmod drv_dht11.ko

测试

./dht11App

测试虽然有读取到温湿度,也有变化,但驱动不是很完美,只限测试使用。

如有侵权,或需要完整代码,请及时联系博主。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

好用的AI搜索引擎

1. 360AI 搜索 访问 360AI 搜索: https://www.huntagi.com/sites/1706642948656.html 360AI 搜索介绍&#xff1a; 360AI 搜索&#xff0c;新一代智能答案引擎&#xff0c;值得信赖的智能搜索伙伴&#xff0c;为复杂搜索提供专业支持&#xff0c;解锁更相关、更全面的答案。AI…

视频汇聚,GB28181,rtsp,rtmp,sip,webrtc,视频点播等多元异构视频融合,视频通话,视频会议交互方案

现在视频汇聚&#xff0c;视频融合和视频互动&#xff0c;是视频技术的应用方向&#xff0c;目前客户一般有很多视频的业务系统&#xff0c;如已有GB28181的监控&#xff08;GB现在是国内主流&#xff0c;大量开源接入和商用方案&#xff09;&#xff0c;rtsp设备&#xff0c;音…

建筑集团工程地产类公司网站源码系统 带完整的安装代码包以及搭建部署教程

系统概述 在数字化浪潮的推动下&#xff0c;建筑行业正经历着前所未有的变革。为了提升企业形象&#xff0c;优化客户体验&#xff0c;加强项目管理&#xff0c;建筑集团工程地产类公司急需一套高效、易用的网站源码系统。小编给大家分享一款专为建筑行业量身定制的网站源码系…

C语言 ——— 在控制台上打印动态变化的菱形

目录 代码要求 代码实现 代码要求 输入 整数line &#xff0c;菱形的上半部分的长度就为line&#xff08;动态变化的菱形&#xff09; 菱形由 "*" 号构成 代码实现 #include<stdio.h> int main() {// 上半长int line 0;scanf("%d", &line)…

MYSQL——库表操作

MYSQL——库表操作 1.1 SQL语句基础1.1.1. SQL简介1.1.2. SQL语句分类1.1.3. SQL语句的书写规范 1.2 数据库的操作1.2.1 数据库的登录及退出1.2.2 查看数据库1.2.3 创建数据库1.2.4 切换数据库1.2.5 查看当前用户1.2.6 删除数据库 1.3 MySQL字符集1.3.1. 字符集1.3.2. 字符序1.…

LeetCode刷题记录(第三天)55. 跳跃游戏

题目&#xff1a; 55. 跳跃游戏 标签&#xff1a;贪心 数组 动态规划 题目信息&#xff1a; 思路一&#xff1a;动态规划 确定dp数组含义&#xff1a; dp[i] 第[i]个位置能否达到确定递推公式&#xff1a; dp[i] 能不能达到&#xff0c;取决于前面d[i-j]&#xff0c;d[i-j…

7月18日学习打卡,数据结构堆

hello大家好呀&#xff0c;本博客目的在于记录暑假学习打卡&#xff0c;后续会整理成一个专栏&#xff0c;主要打算在暑假学习完数据结构&#xff0c;因此会发一些相关的数据结构实现的博客和一些刷的题&#xff0c;个人学习使用&#xff0c;也希望大家多多支持&#xff0c;有不…

SourceCodester v1.0 SQL 注入漏洞(CVE-2023-2130)

前言 CVE-2023-2130是一个影响SourceCodester Purchase Order Management System v1.0的SQL注入漏洞。此漏洞的存在是由于应用程序未能正确过滤和验证用户输入&#xff0c;使得攻击者可以通过SQL注入来执行任意SQL命令&#xff0c;从而对数据库进行未授权的访问和操作。 在利…

java学习--类方法和类方法

类变量 类中static变量会在类加载的时候创建&#xff0c;生存的地方在jdk版本8.0以后都是在堆中&#xff0c;之前都是在方法区&#xff0c;生成的空间不会随着新创建得对象一样变多&#xff0c;同一类的该static变量只会生成一个&#xff0c;每个新创建得对象可共享该变量 类方…

docker compose 容器 编排分组

遇到问题&#xff1a;执行docker compose up -d 后docker compose 创建的容器们 在desktop-docker 中都在docker下一堆 搜索想着能不能把这个docker名字改一下&#xff0c;但是都没有找到这样的一个方案&#xff1b; 最后发现&#xff0c;我执行docker compose up -d 命令所在…

我在百科荣创企业实践——简易函数信号发生器(5)

对于高职教师来说,必不可少的一个任务就是参加企业实践。这个暑假,本人也没闲着,报名参加了上海市电子信息类教师企业实践。7月8日到13日,有幸来到美丽的泉城济南,远离了上海的酷暑,走进了百科荣创科技发展有限公司。在这短短的一周时间里,我结合自己的教学经验和企业的…

前端三大主流框架Vue React Angular有何不同?

前端主流框架&#xff0c;Vue React Angular&#xff0c;大家可能都经常在使用&#xff0c;Vue React&#xff0c;国内用的较多&#xff0c;Angualr相对用的少一点。但是大家有思考过这三大框架的不同吗&#xff1f; 一、项目的选型上 中小型项目&#xff1a;Vue2、React居多…

昇思25天学习打卡营第23天 | 基于MindSpore的红酒分类实验

学习心得&#xff1a;基于MindSpore的红酒分类实验 在机器学习的学习路径中&#xff0c;理解和实践经典算法是非常重要的一步。最近我进行了一个有趣的实验&#xff0c;使用MindSpore框架实现了K近邻&#xff08;KNN&#xff09;算法进行红酒分类。这个实验不仅加深了我对KNN算…

layui 让table里的下拉框不被遮挡

记录&#xff1a;layui 让table里的下拉框不被遮挡 /* 这个是让table里的下拉框不被遮挡 */ .goods_table .layui-select-title,.goods_table .layui-select-title input{line-height: 28px;height: 28px; }.goods_table .layui-table-cell {overflow: visible !important; }.…

用DrissionPage过某里滑块分析

最近我又在找工作了&#xff0c;悲哀啊~&#xff0c;面试官给了一道题&#xff0c;要求如下&#xff1a; 爬虫机试&#xff1a;https://detail.1688.com/offer/643272204627.html 过该链接的滑动验证码&#xff0c;拿到正确的商品信息页html&#xff0c;提取出商品维度的信息&a…

详解SVN与Git相比存在的不足

原文全文详见个人博客&#xff1a; 详解SVN与Git相比存在的不足截至目前&#xff0c;我们已既从整理梳理的SVN和Git在设计理念上的差异&#xff0c;也重点对二者的存储原理和分支管理理念的差异进行深入分析。这些差异也直接造成了SVN和Git在分支合并、冲突解决、历史记录管理…

数据结构之二元查找树转有序双向链表详解与示例(C/C++)

文章目录 1. 二元查找树&#xff08;BST&#xff09;简介2. 有序双向链表&#xff08;DLL&#xff09;简介3. 二元查找树的实现4. 转换为有序双向链表的步骤5. C实现代码6. C实现代码7. 效率与空间复杂度比较8. 结论 在数据结构与算法中&#xff0c;树和链表都是非常重要的数据…

【Linux】进程间通信之-- 共享内存与信号量的介绍(下)

前言 上一篇&#xff0c;我们由进程间通信&#xff0c;引入并讲述了管道、匿名管道和命名管道&#xff0c;本节&#xff0c;将继续学习进程间通信的另一种方式之&#xff0c;共享内存。还要学习几个系统调用接口&#xff0c;并演示两个进程通过共享内存来进行通信。。。 目录 1…

WGS84经纬度坐标 GCJ02火星坐标 BD09百度坐标互相转换

WGS84经纬度坐标 GCJ02火星坐标 BD09百度坐标互相转换 背景&#xff1a;uniapp做的微信小程序&#xff0c;使用到了相机拍照并获取位置坐标信息&#xff1b;在腾讯地图上展示坐标点位置信息&#xff1b; 由于业务需要我们的PC端用的不是腾讯地图&#xff0c;需要使用WGS84坐标或…