SHT30温湿度传感器- 32G12K128 硬件I2C接口 ------32G12K128 驱动教程系列 一、前言 之前看到论坛上有小伙伴留言说想要传感器驱动的教程,那么作为一个热心的STC狂热爱好者来说,必须满足,当然如果屏幕前的看客也需要驱动什么传感器而又不太能实现的话,可以尝试在《STC驱动外围传感器适配汇总意见征集》一贴下面留言,需要的人多的话我会单出一期教程,有什么问题也可以一起交流交流,当然了,按照留言的先后来看,第一个留言的需要个SHT30模块的驱动,那今天就开始慢慢填坑,第一章先开始SHT30模块。PS:这里因为一时疏忽,发现我居然买错了型号,买成了下图模块: 然而实际到货的是这个GXHT30模块,是不是第一篇还没开始就要结束了?不可能的!毕竟某短视频app告诉我们“遇事不要慌,先打开手机拍个朋友圈!”言归正传,其实百度一下问题就直接出来答案了!:lol 可以看到图片里告诉我们GXHT30可以完美的代替SHT30,且性能和通信方式与SHT30完全兼容,那么就是说我们能驱动这个模块了的话,相当于SHT30也就调通了,然后开始我们今天的正文。 提前预告一波今天实现的功能如下所示,可以在串口助手里间隔0.5秒左右打印一次当前的温度和湿度:
二、模块简介 首先下载这个下单链接里的模块资料包,看到里面的内容如下所示: 那首先第一步我们先打开手册查看一下基本的参数。(不论什么模块或者芯片,拿到手的第一件事情就是找到对应的手册,这个上古不变的真理!) 我们找到我们的有用的字眼,这里需要留意下这第一页上的几个关键参数,电源电压可以直接使用5V,I2C接口通信且速度最大可以到1Mhz,GXHT30的典型精度可以到为±3 RH 和±0.3°C。 但是唯一很遗憾的是,这个手册不知道为什么居然连完整的寄存器描述都没有,正版的SHT30的手册我也特地去看了看,也没有关于所有寄存器的一个完整描述,甚至15页的手册,重要的部分竟然只有五页,所以这个模块不打算深入探究,这里只实现最简单的温度和湿度读取。
另外这个传感器表面送了一个防尘透气膜,千万不要去撕掉了哈~他可以保护我们的传感器较少灰尘的损害。
三、 3.1准备工程 为了直接进入正题,这边就不新建工程了,直接从屠龙刀的程序包里复制个这个工程出来,因为这个工程里有我们的usb转cdc的驱动代码,我们最终要把我们传感器的数据通过这个给发送到ISP软件的串口助手上,所以用这个工程改最方便了。
然后我们在这个工程的文件夹里新建两个文件,分别是“HARDWARE”和“SENIOR”,以后我们会把我们单片机的硬件驱动层(IIC,SPI,USART等硬件)程序都放在HARDWARE里,传感器的驱动放在SENIOR里,调用什么类型的传感器就引用一下硬件的驱动,最后文件夹内容如图所示
最后给我们的工程文件夹改个名字,就叫“传感器驱动DEMO”,以后我们所有的传感器驱动的代码都会基于这个上面去扩展,一篇教程给这个工程加一个传感器。
打开这个工程,打开工程框,像图中这样先在工程中新建两个文件夹,对应我们之前在工程目录中建立的“HARDWARE”和“SENIOR”文件夹,这样我们后期工程里的每一个项目文件就会很清晰明了,为我们后期扩展新的功能埋下伏笔。 然后我们编译一下这个工程,看到下图说明编译成功。
3.2移植硬件IIC驱动 在官网的STC32G试验箱的程序里有个“28-I2C主机模式访问AT24C02程序”,我们直接从这个工程里复制出我们需要的部分并加载到我们自己的工程里。先新建文件iic.c和iic.h(讲究模块化编程),都保存到hardware里。
将iic.c添加到hardware中
我们都知道AT24C02是一个典型的IIC器件,那这个程序里就必然有IIC的一个驱动程序。所以先把需要用到的现成的IIC函数,移植到我们的iic.c里面,
由于这个程序里的用户操作函数只有“WriteNbyte”和“ReadNbyte”两个人批量读写多个字节的函数,且只支持8位的地址,我们给他稍微增加一些读写单个字节的函数,另外在增加能读写16位地址的函数,那么IIC.c就变成了下面这样:
- #include "iic.h"
-
-
- void Wait(void)
- {
- while (!(I2CMSST & 0x40));
- I2CMSST &= ~0x40;
- }
-
- void Start(void)
- {
- I2CMSCR = 0x01; //发送START命令
- Wait();
- }
-
- void SendData(char dat)
- {
- I2CTXD = dat; //写数据到数据缓冲区
- I2CMSCR = 0x02; //发送SEND命令
- Wait();
- }
-
- void RecvACK(void)
- {
- I2CMSCR = 0x03; //发送读ACK命令
- Wait();
- }
-
- char RecvData(void)
- {
- I2CMSCR = 0x04; //发送RECV命令
- Wait();
- return I2CRXD;
- }
-
- void SendACK(void)
- {
- I2CMSST = 0x00; //设置ACK信号
- I2CMSCR = 0x05; //发送ACK命令
- Wait();
- }
-
- void SendNAK(void)
- {
- I2CMSST = 0x01; //设置NAK信号
- I2CMSCR = 0x05; //发送ACK命令
- Wait();
- }
-
- void Stop(void)
- {
- I2CMSCR = 0x06; //发送STOP命令
- Wait();
- }
-
- //========================================================================
- // 函数: void IIC_WriteNbyte(u8 slave ,u8 addr, u8 *p, u8 number)
- // 描述: 在一个指定从机设备中,IIC通过一个8位地址连续写多个字节数据
- // 参数: slave :从机地址 addr:8位数据地址 *p:数据保存的地址 number:数据个数
- // 返回: none.
- // 版本: VER1.0
- // 日期: 2022-15-5
- // 备注:
- //========================================================================
- void IIC_WriteNbyte(u8 slave ,u8 addr, u8 *p, u8 number)
- {
- Start(); //发送起始命令
- SendData(slave); //发送设备地址+写命令
- RecvACK();
- SendData(addr); //发送存储地址
- RecvACK();
- do
- {
- SendData(*p++);
- RecvACK();
- }
- while(--number);
- Stop(); //发送停止命令
- }
-
- //========================================================================
- // 函数: void IIC_Writebyte(u8 slave ,u8 addr, u8 dat)
- // 描述: 在一个指定从机设备中,IIC通过一个8位地址写单个字节数据
- // 参数: slave :从机地址 addr:8位数据地址 dat:数据
- // 返回: none.
- // 版本: VER1.0
- // 日期: 2022-15-5
- // 备注:
- //========================================================================
- void IIC_Writebyte(u8 slave ,u8 addr, u8 dat)
- {
- Start(); //发送起始命令
- SendData(slave); //发送设备地址+写命令
- RecvACK();
- SendData(addr); //发送存储地址
- RecvACK();
- SendData(dat);
- RecvACK();
- Stop(); //发送停止命令
- }
-
- //========================================================================
- // 函数: void IIC_ReadNbyte(u8 slave ,u8 addr, u8 *p, u8 number)
- // 描述: 在一个指定从机设备中,IIC通过一个8位地址连续读多个字节数据
- // 参数: slave :从机地址 addr:8位数据地址 *p:数据保存的地址 number:数据个数
- // 返回: none.
- // 版本: VER1.0
- // 日期: 2022-15-5
- // 备注:
- //========================================================================
- void IIC_ReadNbyte(u8 slave ,u8 addr, u8 *p, u8 number)
- {
- Start(); //发送起始命令
- SendData((u8)(slave)); //发送设备地址+写命令
- RecvACK();
- SendData(addr); //发送存储地址
- RecvACK();
- Start(); //发送起始命令
- SendData((u8)(slave+1)); //发送设备地址+读命令
- RecvACK();
- do
- {
- *p = RecvData();
- p++;
- if(number != 1) SendACK(); //send ACK
- }
- while(--number);
- SendNAK(); //send no ACK
- Stop(); //发送停止命令
- }
-
- //========================================================================
- // 函数: void IIC_Readbyte(u8 slave ,u8 addr, u8 *p)
- // 描述: 在一个指定从机设备中,IIC通过一个8位地址读单个字节数据
- // 参数: slave :从机地址 addr:8位数据地址 *p:数据保存的地址 number:数据个数
- // 返回: none.
- // 版本: VER1.0
- // 日期: 2022-15-5
- // 备注:
- //========================================================================
- void IIC_Readbyte(u8 slave ,u8 addr, u8 *p)
- {
- Start(); //发送起始命令
- SendData((u8)(slave)); //发送设备地址+写命令
- RecvACK();
- SendData(addr); //发送存储地址
- RecvACK();
- Start(); //发送起始命令
- SendData((u8)(slave+1)); //发送设备地址+读命令
- RecvACK();
- *p = RecvData();
- SendNAK(); //send no ACK
- Stop(); //发送停止命令
- }
-
- //========================================================================
- // 函数: void IIC_Init( void )
- // 描述: IIC硬件初始化
- // 参数:
- // 返回: none.
- // 版本: VER1.0
- // 日期: 2022-15-5
- // 备注:
- //========================================================================
- void IIC_Init( void )
- {
- I2C_S1 =0; //I2C功能脚选择,00:P1.5,P1.4; 01:P2.5,P2.4; 11:P3.2,P3.3
- I2C_S0 =1;
- I2CCFG = 0xe0; //使能I2C主机模式
- I2CMSST = 0x00;
- }
-
-
-
- //========================================================================
- // 函数: void IIC_ReadNbyte_16bitAddr(u8 slave ,u16 addr, u8 *p, u8 number)
- // 描述: 在一个指定从机设备中,IIC通过一个16位地址连续读多个字节数据
- // 参数: slave :从机地址 addr:16位数据地址 *p:数据保存的地址 number:数据个数
- // 返回: none.
- // 版本: VER1.0
- // 日期: 2022-15-5
- // 备注:
- //========================================================================
- void IIC_ReadNbyte_16bitAddr(u8 slave ,u16 addr, u8 *p, u8 number)
- {
- Start(); //发送起始命令
- SendData((u8)(slave)); //发送设备地址+写命令
- RecvACK();
- SendData((u8)(addr>>8)); //发送存储地址高位
- RecvACK();
- SendData((u8)addr); //发送存储地址低位
- RecvACK();
-
- Start(); //发送起始命令
- SendData((u8)(slave+1)); //发送设备地址+读命令
- RecvACK();
- do
- {
- *p = RecvData();
- p++;
- if(number != 1) SendACK(); //send ACK
- }
- while(--number);
- SendNAK(); //send no ACK
- Stop(); //发送停止命令
- }
-
- //========================================================================
- // 函数: void IIC_WriteNbyte_16bitAddr(u8 slave ,u8 addr, u8 *p, u8 number)
- // 描述: 在一个指定从机设备中,IIC通过一个16位地址连续写多个字节数据
- // 参数: slave :从机地址 addr:16位数据地址 *p:数据保存的地址 number:数据个数
- // 返回: none.
- // 版本: VER1.0
- // 日期: 2022-15-5
- // 备注:
- //========================================================================
- void IIC_WriteNbyte_16bitAddr(u8 slave ,u16 addr, u8 *p, u8 number)
- {
- Start(); //发送起始命令
- SendData(slave); //发送设备地址+写命令
- RecvACK();
- SendData((u8)(addr>>8)); //发送存储地址高位
- RecvACK();
- SendData(addr); //发送存储地址
- RecvACK();
- do
- {
- SendData(*p++);
- RecvACK();
- }
- while(--number);
- Stop(); //发送停止命令
- }
-
- //========================================================================
- // 函数: void IIC_Writebyte_16bitAddr(u8 slave ,u16 addr, u8 dat)
- // 描述: 在一个指定从机设备中,IIC通过一个16位地址写单个字节数据
- // 参数: slave :从机地址 addr:16位数据地址 dat:数据
- // 返回: none.
- // 版本: VER1.0
- // 日期: 2022-15-5
- // 备注:
- //========================================================================
- void IIC_Writebyte_16bitAddr(u8 slave ,u16 addr, u8 dat)
- {
- Start(); //发送起始命令
- SendData(slave); //发送设备地址+写命令
- RecvACK();
- SendData((u8)(addr>>8)); //发送存储地址高位
- RecvACK();
- SendData(addr); //发送存储地址
- RecvACK();
- SendData(dat);
- RecvACK();
- Stop(); //发送停止命令
- }
-
- //========================================================================
- // 函数: void IIC_Readbyte_16bitAddr(u8 slave ,u16 addr, u8 *p)
- // 描述: 在一个指定从机设备中,IIC通过一个16位地址读单个字节数据
- // 参数: slave :从机地址 addr:16位数据地址 *p:数据保存的地址 number:数据个数
- // 返回: none.
- // 版本: VER1.0
- // 日期: 2022-15-5
- // 备注:
- //========================================================================
- void IIC_Readbyte_16bitAddr(u8 slave ,u16 addr, u8 *p)
- {
- Start(); //发送起始命令
- SendData((u8)(slave)); //发送设备地址+写命令
- RecvACK();
- SendData((u8)(addr>>8)); //发送存储地址高位
- RecvACK();
- SendData(addr); //发送存储地址
- RecvACK();
- Start(); //发送起始命令
- SendData((u8)(slave+1)); //发送设备地址+读命令
- RecvACK();
- *p = RecvData();
- SendNAK(); //send no ACK
- Stop(); //发送停止命令
- }
复制代码
同理,IIC.h文件就是这样的了 - #ifndef __IIC_H
- #define __IIC_H
-
- #include "stc.h"
-
- void IIC_Init( void ); //IIC硬件初始化
-
- void Wait(void);
- void Start(void);
- void SendData(char dat);
- void RecvACK(void);
- char RecvData(void);
- void SendACK(void);
- void SendNAK(void);
- void Stop(void);
-
- void IIC_WriteNbyte(u8 slave ,u8 addr, u8 *p, u8 number); //在一个指定从机设备中,IIC通过一个8位地址连续写多个字节数据
- void IIC_ReadNbyte(u8 slave ,u8 addr, u8 *p, u8 number); //在一个指定从机设备中,IIC通过一个8位地址连续读多个字节数据
- void IIC_Writebyte(u8 slave ,u8 addr, u8 dat); //在一个指定从机设备中,IIC通过一个8位地址写单个字节数据
- void IIC_Readbyte(u8 slave ,u8 addr, u8 *p) ; //在一个指定从机设备中,IIC通过一个8位地址读单个字节数据
-
- void IIC_ReadNbyte_16bitAddr(u8 slave ,u16 addr, u8 *p, u8 number); //在一个指定从机设备中,IIC通过一个16位地址连续读多个字节数据
- void IIC_WriteNbyte_16bitAddr(u8 slave ,u16 addr, u8 *p, u8 number); //在一个指定从机设备中,IIC通过一个16位地址连续写多个字节数据
- void IIC_Writebyte_16bitAddr(u8 slave ,u16 addr, u8 dat) ; //在一个指定从机设备中,IIC通过一个16位地址写单个字节数据
- void IIC_Readbyte_16bitAddr(u8 slave ,u16 addr, u8 *p) ; //在一个指定从机设备中,IIC通过一个16位地址读单个字节数据
-
- #endif
-
复制代码
因为这里篇幅有限,我就不详细展开讲解IIC的时序部分了,我们专业的CV工程师驱动部分都是直接复制过来使用的:lol要是真有需要的IIC剖析时序的,请留言。
3.3编写GXHT30/SHT30驱动
按照之前新建IIC.c和IIC.h的办法,我们先新建一个GXHT30.c和GXHT30.h的文件,保存到SENIOR文件夹下,并添加到工程
首先IIC的器件必然有一个器件地址,查阅手册得知:设备地址选择脚ADDR(PIN2)接GND,设备写地址为0x88(0x44<<1)。
其次初始化这个模块,这里查阅手册,看到他可以使用周期转换,说白了就是每隔一定的时间自动转化数据,我们就可以随时去读取数据了。
这里我们选择0.5mps的周期转化率(0.5秒测量一次的一次:lol),重复率选择为高,这样就是往0X20寄存器写入16进制的32就可以了。
其次,这个器件需要CRC的校验,可以看到手册的这个位置:
按照常理来说,手册会给出这个校验方法的C语言实现过程,不过这个手册关于CRC的部分在手册里篇幅就这么不到40CM²,为了节省篇幅这里我们还是直接从网上复制一个现成的过来就好了,不详细展开叙述了,具体的实现过程可以百度一下,非常多。 - //========================================================================
- // 函数: int CRC8_Compute(uchar *check_data, uchar num_of_data)
- // 描述: CRC数据校验
- // 参数:
- // 返回: none.
- // 版本: VER1.0
- // 日期: 2022-15-5
- // 备注:
- //========================================================================
- int CRC8_Compute(uchar *check_data, uchar num_of_data)
- {
- uchar i; // bit mask
- uchar crc = 0xFF; // calculated checksum
- uchar byteCtr; // byte counter
-
- // calculates 8-Bit checksum with given polynomial
- for(byteCtr = 0; byteCtr < num_of_data; byteCtr++)
- {
- crc ^= (check_data[byteCtr]);//异或赋值,crc=crc^(check_data[byteCtr])
-
- //crc校验,最高位是1就^0x31
- for(i = 8; i > 0; --i)
- {
- if(crc & 0x80)
- {
- crc = (crc << 1) ^ 0x31;
- }
- else
- {
- crc = (crc << 1);
- }
- }
- }
- return crc;
- }
复制代码
有了如上这些前提准备,然后我们就可以开始读取最终的温湿度数据了,看到手册,周期性测量的温湿度读取办法如下
可以看到这个通信的逻辑刚刚好就是我们的“IIC_ReadNbyte_16bitAddr”函数对应的逻辑,先写入设备地址,在写入16位的数据地址,在发送读取命令,然后连续读取6个字节,且这6个字节的数据分别是温度高位,温度低位,温度CRC校验,湿度高位,湿度低位,湿度CRC校验。
然后我们校验完CRC正确之后就可以开始计算最终的温度了。
具体的换算公式如上图所示,图中St就是16位的温度数据,Srh就是16位的湿度数据,这样就根据这个公式算出最后的温湿度了。
经过如上分析,最终的GXHT3.c文件如下所示: - #include "GXHT30.h"
-
- //基本数据:
- //供电范围:2.4V~5.5V,湿度误差±1.5,温度误差±0.2。
- //设备地址选择脚,ADDR(PIN2)接GND,设备写地址为0x88(0x44<<1),读地址为0x89(0x45<<1)。
- //读取的信息顺序是:温度数据(16位)+校验和(8位)+适度数据(16位)+校验和(8位)。
- //湿度:RH=100*(1.0*SRH / 65535),摄氏温度:T=-45+175*(1.0*ST / 65535);
-
- u16 SHT30_temperature; //温度数据 1位小数
- u16 SHT30_humidity; //湿度数据 1位小数
- u8 read_sht_data[6]; //数据读取缓存
-
-
- //========================================================================
- // 函数: void SHT30_Init(void)
- // 描述: SHT30传感器初始化
- // 参数:
- // 返回: none.
- // 版本: VER1.0
- // 日期: 2022-15-5
- // 备注:
- //========================================================================
- void SHT30_Init(void)
- {
- IIC_Writebyte(GXHT30_Slave,0x20,0x32);
- }
-
-
- //========================================================================
- // 函数: int CRC8_Compute(uchar *check_data, uchar num_of_data)
- // 描述: CRC数据校验
- // 参数:
- // 返回: none.
- // 版本: VER1.0
- // 日期: 2022-15-5
- // 备注:
- //========================================================================
- int CRC8_Compute(uchar *check_data, uchar num_of_data)
- {
- uchar i; // bit mask
- uchar crc = 0xFF; // calculated checksum
- uchar byteCtr; // byte counter
-
- // calculates 8-Bit checksum with given polynomial
- for(byteCtr = 0; byteCtr < num_of_data; byteCtr++)
- {
- crc ^= (check_data[byteCtr]);//异或赋值,crc=crc^(check_data[byteCtr])
-
- //crc校验,最高位是1就^0x31
- for(i = 8; i > 0; --i)
- {
- if(crc & 0x80)
- {
- crc = (crc << 1) ^ 0x31;
- }
- else
- {
- crc = (crc << 1);
- }
- }
- }
- return crc;
- }
-
- //========================================================================
- // 函数: int CRC8_Compute(uchar *check_data, uchar num_of_data)
- // 描述: CRC数据校验
- // 参数:
- // 返回: none.
- // 版本: VER1.0
- // 日期: 2022-15-5
- // 备注:
- //========================================================================
- int SHT30_CRC8_Check(uchar *p,uchar num_of_data,uchar CrcData)
- {
- uchar crc;
- crc = CRC8_Compute(p, num_of_data);// calculates 8-Bit checksum
- if(crc != CrcData)
- {
- return 1;
- }
- return 0;
- }
-
- //========================================================================
- // 函数: void SHT30_DataRead(void)
- // 描述: 温湿度数据读取
- // 参数:
- // 返回: none.
- // 版本: VER1.0
- // 日期: 2022-15-5
- // 备注:
- //========================================================================
- void SHT30_DataRead(void)
- {
- uchar temporary[3]; //用于暂时存放总线读出的温湿度值
- uint Data_convert; //用于数据转换
- uchar crc_result; //用于CRC校验结果存放,为判断数据准确性做准备
-
- //read_sht30(read_sht_data,6); //读出数据放入缓存数组等待处理>>T高八位>>T低八位>>温度T的CRC校验位>>H高八位>>H低八位>>湿度H的CRC校验位
-
- IIC_ReadNbyte_16bitAddr(GXHT30_Slave,0XE000,read_sht_data,6);
-
- temporary[0]=read_sht_data[0];//温度数据高八位
- temporary[1]=read_sht_data[1];//温度数据低八位
- temporary[2]=read_sht_data[2];//温度数据CRC校验位
-
- crc_result=SHT30_CRC8_Check(temporary,2,temporary[2]); //crc校验,crc校验要是不成功就返回1,
- //同时不会更新温度值
- if(crc_result==0)
- {
- Data_convert=(uint)(temporary[0] << 8) | temporary[1]; //把2个8位数据拼接为一个16位的数据
- //温度转换,将16位温度数据转化为10进制的温度数据,
- //这里保留了一位小数,SHT30_temperature这是一个全局变量,
- //计算温度值(uchar强制转换,数据在超过八位范围后会丢失)
- SHT30_temperature = (uint)((175.0 * (float)(0x7fff&Data_convert) / 65535.0 - 45.0) *10.0);
- }
-
- temporary[0]=read_sht_data[3];//湿度数据高八位
- temporary[1]=read_sht_data[4];//湿度数据低八位
- temporary[2]=read_sht_data[5];//湿度数据CRC校验位
-
- //crc校验
- crc_result=SHT30_CRC8_Check(temporary,2,temporary[2]); //crc校验,crc校验要是不成功就返回1,
- //同时不会更新湿度值
- if(crc_result==0)
- {
- Data_convert=(uint)(temporary[0] << 8) | temporary[1];
- //湿度转换,将16位湿度数据转化为10进制的湿度数据,
- //这里保留了一位小数,SHT30_humidity这是一个全局变量,
- //计算湿度值(uchar强制转换,数据在超过八位范围后会丢失)
- SHT30_humidity = (uint)((100.0 * (float)(0x7fff&Data_convert) / 65535.0) *10.0);
- }
- }
复制代码
最终的GXHT3.h文件如下所示: - #ifndef __GXHT30_H
- #define __GXHT30_H
-
- #include "iic.h"
-
- #define GXHT30_Slave 0x88 //器件的IIC地址
-
- extern u16 SHT30_temperature;
- extern u16 SHT30_humidity;
-
- void SHT30_Init(void);
- void SHT30_DataRead(void);
-
-
-
- #endif
复制代码
最后改写一下main.c里的程序,最后的main.c如下所示 - /*---------------------------------------------------------------------*/
- /* --- STC MCU Limited ------------------------------------------------*/
- /* --- STC 1T Series MCU Demo Programme -------------------------------*/
- /* --- Mobile: (86)13922805190 ----------------------------------------*/
- /* --- Fax: 86-0513-55012956,55012947,55012969 ------------------------*/
- /* --- Tel: 86-0513-55012928,55012929,55012966 ------------------------*/
- /* --- Web: www.STCMCU.com --------------------------------------------*/
- /* --- Web: www.STCMCUDATA.com ---------------------------------------*/
- /* --- QQ: 800003751 -------------------------------------------------*/
- /* 如果要在程序中使用此代码,请在程序中注明使用了STC的资料及程序 */
- /*---------------------------------------------------------------------*/
-
- /************* 功能说明 **************
-
- 本例程基于STC32G核心转接板(屠龙刀)进行编写测试。
-
- 使用USB线连接核心板USB接口与电脑;
-
- MCU通过USB CDC(Communication Device Class)协议识别为串口设备;
-
- 使用串口助手打开CDC虚拟串口,然后向MCU发送数据,MCU返回接收到的数据到串口助手;
-
- 数据长度限制在63字节以内。
-
- 此外程序演示两种复位进入USB下载模式的方法:
- 1. 通过每1毫秒执行一次“KeyResetScan”函数,实现长按P3.2口按键触发MCU复位,进入USB下载模式。
- (如果不希望复位进入USB下载模式的话,可在复位代码里将 IAP_CONTR 的bit6清0,选择复位进用户程序区)
- 2. 通过加载“stc_usb_cdc_32g.lib”库函数,实现使用STC-ISP软件发送指令触发MCU复位,进入USB下载模式并自动下载。
- (注意:使用CDC接口触发MCU复位并自动下载功能,需要勾选端口设置:下次强制使用”STC USB Writer (HID)”进行ISP下载)
-
- 下载时, 选择时钟 24MHZ (用户可自行修改频率)。
-
- ******************************************/
-
- #include "stc.h"
- #include "usb.h"
- #include "GXHT30.h"
-
- #define MAIN_Fosc 24000000L //定义主时钟
-
- char *USER_DEVICEDESC = NULL;
- char *USER_PRODUCTDESC = NULL;
- char *USER_STCISPCMD = "@STCISP#"; //设置自动复位到ISP区的用户接口命令
-
- //P3.2口按键复位所需变量
- bit Key_Flag;
- u16 Key_cnt;
-
- void sys_init();
- void delay_ms(u8 ms);
- void KeyResetScan(void);
-
- void main()
- {
- u32 count = 0;
- sys_init(); //系统初始化
- usb_init(); //USB CDC 接口配置
-
- IIC_Init();
- SHT30_Init();
- EA = 1;
-
- while (1)
- {
- delay_ms(1);
- KeyResetScan(); //长按P3.2口按键触发软件复位,进入USB下载模式,不需要此功能可删除本行代码
-
- if(DeviceState != DEVSTATE_CONFIGURED) //等待USB完成配置
- continue;
-
- if (bUsbOutReady)
- {
- usb_OUT_done(); //接收应答(固定格式)
-
- printf("OutNumber=0x%X\r\n",OutNumber); //使用 printf 函数打印接收数据长度
-
- memcpy(UsbInBuffer, UsbOutBuffer, OutNumber); //将接收数据(UsbOutBuffer),复制到发送缓冲区(UsbInBuffer)
- usb_IN(OutNumber); //原路返回, 用于测试
- }
- delay_ms(250);delay_ms(250);
- SHT30_DataRead();
- printf("温度:%.1f℃\t湿度:%.1f%%\r\n",(float)SHT30_temperature/10,(float)SHT30_humidity/10);
- }
- }
-
- void sys_init()
- {
- WTST = 0; //设置程序指令延时参数,赋值为0可将CPU执行指令的速度设置为最快
- EAXFR = 1; //扩展寄存器(XFR)访问使能
- CKCON = 0; //提高访问XRAM速度
-
- P0M1 = 0x00; P0M0 = 0x00; //设置为准双向口
- P1M1 = 0x00; P1M0 = 0x00; //设置为准双向口
- P2M1 = 0x00; P2M0 = 0x00; //设置为准双向口
- P3M1 = 0x00; P3M0 = 0x00; //设置为准双向口
- P4M1 = 0x00; P4M0 = 0x00; //设置为准双向口
- P5M1 = 0x00; P5M0 = 0x00; //设置为准双向口
- P6M1 = 0x00; P6M0 = 0x00; //设置为准双向口
- P7M1 = 0x00; P7M0 = 0x00; //设置为准双向口
-
- //====== USB 初始化 ======
- P3M0 &= ~0x03;
- P3M1 |= 0x03;
-
- IRC48MCR = 0x80;
- while (!(IRC48MCR & 0x01));
-
- USBCLK = 0x00;
- USBCON = 0x90;
- //========================
- }
-
- //========================================================================
- // 函数: void delay_ms(u8 ms)
- // 描述: 延时函数。
- // 参数: ms,要延时的ms数, 这里只支持1~255ms. 自动适应主时钟.
- // 返回: none.
- // 版本: VER1.0
- // 日期: 2021-3-9
- // 备注:
- //========================================================================
- void delay_ms(u8 ms)
- {
- u16 i;
- do{
- i = MAIN_Fosc / 6000;
- while(--i); //6T per loop
- }while(--ms);
- }
-
- //========================================================================
- // 函数: void KeyResetScan(void)
- // 描述: P3.2口按键长按1秒触发软件复位,进入USB下载模式。
- // 参数: none.
- // 返回: none.
- // 版本: VER1.0
- // 日期: 2022-6-11
- // 备注:
- //========================================================================
- void KeyResetScan(void)
- {
- if(!P32)
- {
- if(!Key_Flag)
- {
- Key_cnt++;
- if(Key_cnt >= 1000) //连续1000ms有效按键检测
- {
- Key_Flag = 1; //设置按键状态,防止重复触发
-
- USBCON = 0x00; //清除USB设置
- USBCLK = 0x00;
- IRC48MCR = 0x00;
-
- delay_ms(10);
- IAP_CONTR = 0x60; //触发软件复位,从ISP开始执行
- while (1);
- }
- }
- }
- else
- {
- Key_cnt = 0;
- Key_Flag = 0;
- }
- }
复制代码
这段代码主要就是在while函数里添加了温湿度读取的指令,然后通过现成的PRINTF函数给他直接打印了出来,这里需要注意一下因为我们采样率设置的0.5mps,所以我们需要间隔500ms以上读取一次,这里为了方便理解直接两个250ms的delay实现了,移植到自己程序的时候可以加个delay。最终实现的效果如下,当然也能看帖子最前面的GIF。
这里加上一个接线图连接示意图哈! MCU(STC32G) | GXHT30 | 5V | VCC | GND | GND | P2.4 | SDA | P2.5 | SCL |
看完了文章,觉得有用的请在文末点个赞哈哈;P由于附件的空间受限,需要上述所需文件的请进群下载~
|