请选择 进入手机版 | 继续访问电脑版

国芯论坛-STC全球32位8051爱好者互助交流社区

 找回密码
 立即注册
查看: 3989|回复: 17

SHT30温湿度传感器, 硬件I2C接口, STC驱动教程系列

[复制链接]
  • TA的每日心情
    开心
    昨天 10:10
  • 签到天数: 47 天

    [LV.5]常住居民I

    26

    主题

    784

    回帖

    5843

    积分

    荣誉版主

    冲哥视频教程和各种开源资料QQ交流群884047237,可群

    积分
    5843
    QQ
    发表于 2022-12-6 17:55:48 | 显示全部楼层 |阅读模式
    SHT30温湿度传感器-STC32硬件I2C接口
    ------STC驱动教程系列
    一、前言
      之前看到论坛上有小伙伴留言说想要传感器驱动的教程,那么作为一个热心的STC狂热爱好者来说,必须满足,当然如果屏幕前的看客也需要驱动什么传感器而又不太能实现的话,可以尝试在《STC驱动外围传感器适配汇总意见征集一贴下面留言,需要的人多的话我会单出一期教程,有什么问题也可以一起交流交流,当然了,按照留言的先后来看,第一个留言的需要个SHT30模块的驱动,那今天就开始慢慢填坑,第一章先开始SHT30模块。PS:这里因为一时疏忽,发现我居然买错了型号,买成了下图模块:
    图片1.png
      然而实际到货的是这个GXHT30模块,是不是第一篇还没开始就要结束了?不可能的!毕竟某短视频app告诉我们“遇事不要慌,先打开手机拍个朋友圈!”言归正传,其实百度一下问题就直接出来答案了!:lol
    图片2.png
      可以看到图片里告诉我们GXHT30可以完美的代替SHT30,且性能和通信方式与SHT30完全兼容,那么就是说我们能驱动这个模块了的话,相当于SHT30也就调通了,然后开始我们今天的正文。
    提前预告一波今天实现的功能如下所示,可以在串口助手里间隔0.5秒左右打印一次当前的温度和湿度:
    1670313686453.gif

    二、模块简介
      首先下载这个下单链接里的模块资料包,看到里面的内容如下所示:
    图片3.png
      那首先第一步我们先打开手册查看一下基本的参数。(不论什么模块或者芯片,拿到手的第一件事情就是找到对应的手册,这个上古不变的真理!)
    图片4.png
      我们找到我们的有用的字眼,这里需要留意下这第一页上的几个关键参数,电源电压可以直接使用5V,I2C接口通信且速度最大可以到1Mhz,GXHT30的典型精度可以到为±3 RH 和±0.3°C。
    插图.png
      但是唯一很遗憾的是,这个手册不知道为什么居然连完整的寄存器描述都没有,正版的SHT30的手册我也特地去看了看,也没有关于所有寄存器的一个完整描述,甚至15页的手册,重要的部分竟然只有五页,所以这个模块不打算深入探究,这里只实现最简单的温度和湿度读取。

    图片5.png
      另外这个传感器表面送了一个防尘透气膜,千万不要去撕掉了哈~他可以保护我们的传感器较少灰尘的损害。

    三、
    3.1准备工程
      为了直接进入正题,这边就不新建工程了,直接从屠龙刀的程序包里复制个这个工程出来,因为这个工程里有我们的usb转cdc的驱动代码,我们最终要把我们传感器的数据通过这个给发送到ISP软件的串口助手上,所以用这个工程改最方便了。
    QQ截图20221206172929.png

      然后我们在这个工程的文件夹里新建两个文件,分别是“HARDWARE”和“SENIOR”,以后我们会把我们单片机的硬件驱动层(IIC,SPI,USART等硬件)程序都放在HARDWARE里,传感器的驱动放在SENIOR里,调用什么类型的传感器就引用一下硬件的驱动,最后文件夹内容如图所示
    QQ截图20221206173549.png

      最后给我们的工程文件夹改个名字,就叫“传感器驱动DEMO”,以后我们所有的传感器驱动的代码都会基于这个上面去扩展,一篇教程给这个工程加一个传感器。
    图片6.png


      打开这个工程,打开工程框,像图中这样先在工程中新建两个文件夹,对应我们之前在工程目录中建立的“HARDWARE”和“SENIOR”文件夹,这样我们后期工程里的每一个项目文件就会很清晰明了,为我们后期扩展新的功能埋下伏笔。
    图片7.png
      然后我们编译一下这个工程,看到下图说明编译成功。
    图片8.png

    3.2移植硬件IIC驱动
      在官网的STC32G试验箱的程序里有个“28-I2C主机模式访问AT24C02程序”,我们直接从这个工程里复制出我们需要的部分并加载到我们自己的工程里。先新建文件iic.ciic.h(讲究模块化编程),都保存到hardware里。
    图片9.png

      将iic.c添加到hardware中
    图片10.png

      我们都知道AT24C02是一个典型的IIC器件,那这个程序里就必然有IIC的一个驱动程序。所以先把需要用到的现成的IIC函数,移植到我们的iic.c里面,
    QQ截图20221207094757.png
      由于这个程序里的用户操作函数只有“WriteNbyte”和“ReadNbyte”两个人批量读写多个字节的函数,且只支持8位的地址,我们给他稍微增加一些读写单个字节的函数,另外在增加能读写16位地址的函数,那么IIC.c就变成了下面这样:

    1. #include "iic.h"
    2. void Wait(void)
    3. {
    4.     while (!(I2CMSST & 0x40));
    5.     I2CMSST &= ~0x40;
    6. }
    7. void Start(void)
    8. {
    9.     I2CMSCR = 0x01;                         //发送START命令
    10.     Wait();
    11. }
    12. void SendData(char dat)
    13. {
    14.     I2CTXD = dat;                           //写数据到数据缓冲区
    15.     I2CMSCR = 0x02;                         //发送SEND命令
    16.     Wait();
    17. }
    18. void RecvACK(void)
    19. {
    20.     I2CMSCR = 0x03;                         //发送读ACK命令
    21.     Wait();
    22. }
    23. char RecvData(void)
    24. {
    25.     I2CMSCR = 0x04;                         //发送RECV命令
    26.     Wait();
    27.     return I2CRXD;
    28. }
    29. void SendACK(void)
    30. {
    31.     I2CMSST = 0x00;                         //设置ACK信号
    32.     I2CMSCR = 0x05;                         //发送ACK命令
    33.     Wait();
    34. }
    35. void SendNAK(void)
    36. {
    37.     I2CMSST = 0x01;                         //设置NAK信号
    38.     I2CMSCR = 0x05;                         //发送ACK命令
    39.     Wait();
    40. }
    41. void Stop(void)
    42. {
    43.     I2CMSCR = 0x06;                         //发送STOP命令
    44.     Wait();
    45. }
    46. //========================================================================
    47. // 函数: void IIC_WriteNbyte(u8 slave ,u8 addr, u8 *p, u8 number)  
    48. // 描述: 在一个指定从机设备中,IIC通过一个8位地址连续写多个字节数据
    49. // 参数: slave :从机地址   addr:8位数据地址  *p:数据保存的地址   number:数据个数
    50. // 返回: none.
    51. // 版本: VER1.0
    52. // 日期: 2022-15-5
    53. // 备注:
    54. //========================================================================
    55. void IIC_WriteNbyte(u8 slave ,u8 addr, u8 *p, u8 number)  
    56. {
    57.     Start();                                //发送起始命令
    58.     SendData(slave);                         //发送设备地址+写命令
    59.     RecvACK();
    60.     SendData(addr);                         //发送存储地址
    61.     RecvACK();
    62.     do
    63.     {
    64.         SendData(*p++);
    65.         RecvACK();
    66.     }
    67.     while(--number);
    68.     Stop();                                 //发送停止命令
    69. }
    70. //========================================================================
    71. // 函数: void IIC_Writebyte(u8 slave ,u8 addr, u8 dat)
    72. // 描述: 在一个指定从机设备中,IIC通过一个8位地址写单个字节数据
    73. // 参数: slave :从机地址   addr:8位数据地址  dat:数据
    74. // 返回: none.
    75. // 版本: VER1.0
    76. // 日期: 2022-15-5
    77. // 备注:
    78. //========================================================================
    79. void IIC_Writebyte(u8 slave ,u8 addr, u8 dat)
    80. {
    81.     Start();                                //发送起始命令
    82.     SendData(slave);                         //发送设备地址+写命令
    83.     RecvACK();
    84.     SendData(addr);                         //发送存储地址
    85.     RecvACK();
    86.     SendData(dat);
    87.     RecvACK();
    88.     Stop();                                 //发送停止命令
    89. }
    90. //========================================================================
    91. // 函数: void IIC_ReadNbyte(u8 slave ,u8 addr, u8 *p, u8 number)   
    92. // 描述: 在一个指定从机设备中,IIC通过一个8位地址连续读多个字节数据
    93. // 参数: slave :从机地址   addr:8位数据地址  *p:数据保存的地址   number:数据个数
    94. // 返回: none.
    95. // 版本: VER1.0
    96. // 日期: 2022-15-5
    97. // 备注:
    98. //========================================================================
    99. void IIC_ReadNbyte(u8 slave ,u8 addr, u8 *p, u8 number)   
    100. {
    101.     Start();                                //发送起始命令
    102.     SendData((u8)(slave));                         //发送设备地址+写命令
    103.     RecvACK();
    104.     SendData(addr);                         //发送存储地址
    105.     RecvACK();
    106.     Start();                                //发送起始命令
    107.     SendData((u8)(slave+1));                         //发送设备地址+读命令
    108.     RecvACK();
    109.     do
    110.     {
    111.         *p = RecvData();
    112.         p++;
    113.         if(number != 1) SendACK();          //send ACK
    114.     }
    115.     while(--number);
    116.     SendNAK();                              //send no ACK        
    117.     Stop();                                 //发送停止命令
    118. }
    119. //========================================================================
    120. // 函数: void IIC_Readbyte(u8 slave ,u8 addr, u8 *p)  
    121. // 描述: 在一个指定从机设备中,IIC通过一个8位地址读单个字节数据
    122. // 参数: slave :从机地址   addr:8位数据地址  *p:数据保存的地址   number:数据个数
    123. // 返回: none.
    124. // 版本: VER1.0
    125. // 日期: 2022-15-5
    126. // 备注:
    127. //========================================================================
    128. void IIC_Readbyte(u8 slave ,u8 addr, u8 *p)  
    129. {
    130.     Start();                                //发送起始命令
    131.     SendData((u8)(slave));                         //发送设备地址+写命令
    132.     RecvACK();
    133.     SendData(addr);                         //发送存储地址
    134.     RecvACK();
    135.     Start();                                //发送起始命令
    136.     SendData((u8)(slave+1));                         //发送设备地址+读命令
    137.     RecvACK();
    138.     *p = RecvData();
    139.     SendNAK();                              //send no ACK        
    140.     Stop();                                 //发送停止命令
    141. }
    142. //========================================================================
    143. // 函数: void IIC_Init( void )
    144. // 描述: IIC硬件初始化
    145. // 参数:
    146. // 返回: none.
    147. // 版本: VER1.0
    148. // 日期: 2022-15-5
    149. // 备注:
    150. //========================================================================
    151. void IIC_Init( void )
    152. {
    153.     I2C_S1 =0;   //I2C功能脚选择,00:P1.5,P1.4; 01:P2.5,P2.4; 11:P3.2,P3.3
    154.     I2C_S0 =1;
    155.     I2CCFG = 0xe0;              //使能I2C主机模式
    156.     I2CMSST = 0x00;   
    157. }
    158. //========================================================================
    159. // 函数: void IIC_ReadNbyte_16bitAddr(u8 slave ,u16 addr, u8 *p, u8 number)     
    160. // 描述: 在一个指定从机设备中,IIC通过一个16位地址连续读多个字节数据
    161. // 参数: slave :从机地址   addr:16位数据地址  *p:数据保存的地址   number:数据个数
    162. // 返回: none.
    163. // 版本: VER1.0
    164. // 日期: 2022-15-5
    165. // 备注:
    166. //========================================================================
    167. void IIC_ReadNbyte_16bitAddr(u8 slave ,u16 addr, u8 *p, u8 number)   
    168. {
    169.     Start();                                //发送起始命令
    170.     SendData((u8)(slave));                         //发送设备地址+写命令
    171.     RecvACK();
    172.     SendData((u8)(addr>>8));                         //发送存储地址高位
    173.     RecvACK();
    174.     SendData((u8)addr);                         //发送存储地址低位
    175.     RecvACK();   
    176.    
    177.     Start();                                //发送起始命令
    178.     SendData((u8)(slave+1));                         //发送设备地址+读命令
    179.     RecvACK();
    180.     do
    181.     {
    182.         *p = RecvData();
    183.         p++;
    184.         if(number != 1) SendACK();          //send ACK
    185.     }
    186.     while(--number);
    187.     SendNAK();                              //send no ACK        
    188.     Stop();                                 //发送停止命令
    189. }
    190. //========================================================================
    191. // 函数: void IIC_WriteNbyte_16bitAddr(u8 slave ,u8 addr, u8 *p, u8 number)  
    192. // 描述: 在一个指定从机设备中,IIC通过一个16位地址连续写多个字节数据
    193. // 参数: slave :从机地址   addr:16位数据地址  *p:数据保存的地址   number:数据个数
    194. // 返回: none.
    195. // 版本: VER1.0
    196. // 日期: 2022-15-5
    197. // 备注:
    198. //========================================================================
    199. void IIC_WriteNbyte_16bitAddr(u8 slave ,u16 addr, u8 *p, u8 number)  
    200. {
    201.     Start();                                //发送起始命令
    202.     SendData(slave);                         //发送设备地址+写命令
    203.     RecvACK();
    204.     SendData((u8)(addr>>8));                         //发送存储地址高位
    205.     RecvACK();   
    206.     SendData(addr);                         //发送存储地址
    207.     RecvACK();
    208.     do
    209.     {
    210.         SendData(*p++);
    211.         RecvACK();
    212.     }
    213.     while(--number);
    214.     Stop();                                 //发送停止命令
    215. }
    216. //========================================================================
    217. // 函数: void IIC_Writebyte_16bitAddr(u8 slave ,u16 addr, u8 dat)
    218. // 描述: 在一个指定从机设备中,IIC通过一个16位地址写单个字节数据
    219. // 参数: slave :从机地址   addr:16位数据地址  dat:数据
    220. // 返回: none.
    221. // 版本: VER1.0
    222. // 日期: 2022-15-5
    223. // 备注:
    224. //========================================================================
    225. void IIC_Writebyte_16bitAddr(u8 slave ,u16 addr, u8 dat)
    226. {
    227.     Start();                                //发送起始命令
    228.     SendData(slave);                         //发送设备地址+写命令
    229.     RecvACK();
    230.     SendData((u8)(addr>>8));                         //发送存储地址高位
    231.     RecvACK();        
    232.     SendData(addr);                         //发送存储地址
    233.     RecvACK();
    234.     SendData(dat);
    235.     RecvACK();
    236.     Stop();                                 //发送停止命令
    237. }
    238. //========================================================================
    239. // 函数: void IIC_Readbyte_16bitAddr(u8 slave ,u16 addr, u8 *p)
    240. // 描述: 在一个指定从机设备中,IIC通过一个16位地址读单个字节数据
    241. // 参数: slave :从机地址   addr:16位数据地址  *p:数据保存的地址   number:数据个数
    242. // 返回: none.
    243. // 版本: VER1.0
    244. // 日期: 2022-15-5
    245. // 备注:
    246. //========================================================================
    247. void IIC_Readbyte_16bitAddr(u8 slave ,u16 addr, u8 *p)  
    248. {
    249.     Start();                                //发送起始命令
    250.     SendData((u8)(slave));                         //发送设备地址+写命令
    251.     RecvACK();
    252.     SendData((u8)(addr>>8));                         //发送存储地址高位
    253.     RecvACK();         
    254.     SendData(addr);                         //发送存储地址
    255.     RecvACK();
    256.     Start();                                //发送起始命令
    257.     SendData((u8)(slave+1));                         //发送设备地址+读命令
    258.     RecvACK();
    259.     *p = RecvData();
    260.     SendNAK();                              //send no ACK        
    261.     Stop();                                 //发送停止命令
    262. }
    复制代码


      同理,IIC.h文件就是这样的了
    1. #ifndef __IIC_H
    2. #define __IIC_H
    3. #include "stc.h"
    4. void IIC_Init( void );                                                              //IIC硬件初始化
    5. void Wait(void);
    6. void Start(void);
    7. void SendData(char dat);
    8. void RecvACK(void);
    9. char RecvData(void);
    10. void SendACK(void);
    11. void SendNAK(void);
    12. void Stop(void);
    13. void IIC_WriteNbyte(u8 slave ,u8 addr, u8 *p, u8 number);                           //在一个指定从机设备中,IIC通过一个8位地址连续写多个字节数据
    14. void IIC_ReadNbyte(u8 slave ,u8 addr, u8 *p, u8 number);                            //在一个指定从机设备中,IIC通过一个8位地址连续读多个字节数据
    15. void IIC_Writebyte(u8 slave ,u8 addr, u8 dat);                                      //在一个指定从机设备中,IIC通过一个8位地址写单个字节数据
    16. void IIC_Readbyte(u8 slave ,u8 addr, u8 *p) ;                                       //在一个指定从机设备中,IIC通过一个8位地址读单个字节数据
    17. void IIC_ReadNbyte_16bitAddr(u8 slave ,u16 addr, u8 *p, u8 number);                 //在一个指定从机设备中,IIC通过一个16位地址连续读多个字节数据
    18. void IIC_WriteNbyte_16bitAddr(u8 slave ,u16 addr, u8 *p, u8 number);                //在一个指定从机设备中,IIC通过一个16位地址连续写多个字节数据
    19. void IIC_Writebyte_16bitAddr(u8 slave ,u16 addr, u8 dat) ;                          //在一个指定从机设备中,IIC通过一个16位地址写单个字节数据
    20. void IIC_Readbyte_16bitAddr(u8 slave ,u16 addr, u8 *p)  ;                           //在一个指定从机设备中,IIC通过一个16位地址读单个字节数据   
    21.    
    22. #endif
    复制代码
      因为这里篇幅有限,我就不详细展开讲解IIC的时序部分了,我们专业的CV工程师驱动部分都是直接复制过来使用的:lol要是真有需要的IIC剖析时序的,请留言。




    3.3编写GXHT30/SHT30驱动

      按照之前新建IIC.c和IIC.h的办法,我们先新建一个GXHT30.c和GXHT30.h的文件,保存到SENIOR文件夹下,并添加到工程


    QQ截图20221207103905.png


      首先IIC的器件必然有一个器件地址,查阅手册得知:设备地址选择脚ADDR(PIN2)接GND,设备写地址为0x88(0x44<<1)。


    QQ截图20221207102530.png


      其次初始化这个模块,这里查阅手册,看到他可以使用周期转换,说白了就是每隔一定的时间自动转化数据,我们就可以随时去读取数据了。


    QQ截图20221207100347.png
      这里我们选择0.5mps的周期转化率(0.5秒测量一次的一次:lol),重复率选择为高,这样就是往0X20寄存器写入16进制的32就可以了。



      其次,这个器件需要CRC的校验,可以看到手册的这个位置:


    QQ截图20221207101036.png
      按照常理来说,手册会给出这个校验方法的C语言实现过程,不过这个手册关于CRC的部分在手册里篇幅就这么不到40CM²,为了节省篇幅这里我们还是直接从网上复制一个现成的过来就好了,不详细展开叙述了,具体的实现过程可以百度一下,非常多。
    1. //========================================================================
    2. // 函数: int  CRC8_Compute(uchar *check_data, uchar num_of_data)
    3. // 描述: CRC数据校验
    4. // 参数:
    5. // 返回: none.
    6. // 版本: VER1.0
    7. // 日期: 2022-15-5
    8. // 备注:
    9. //========================================================================
    10. int  CRC8_Compute(uchar *check_data, uchar num_of_data)
    11. {
    12.     uchar i;        // bit mask
    13.     uchar crc = 0xFF; // calculated checksum
    14.     uchar byteCtr;    // byte counter
    15.     // calculates 8-Bit checksum with given polynomial
    16.     for(byteCtr = 0; byteCtr < num_of_data; byteCtr++)
    17.     {
    18.         crc ^= (check_data[byteCtr]);//异或赋值,crc=crc^(check_data[byteCtr])
    19.         //crc校验,最高位是1就^0x31
    20.         for(i = 8; i > 0; --i)
    21.         {
    22.             if(crc & 0x80)
    23.             {
    24.                 crc = (crc << 1) ^ 0x31;
    25.             }  
    26.             else
    27.             {
    28.                 crc = (crc << 1);
    29.             }
    30.         }
    31.     }
    32.     return crc;
    33. }
    复制代码



      有了如上这些前提准备,然后我们就可以开始读取最终的温湿度数据了,看到手册,周期性测量的温湿度读取办法如下


    读取1.png

    读取2.png

      可以看到这个通信的逻辑刚刚好就是我们的“IIC_ReadNbyte_16bitAddr”函数对应的逻辑,先写入设备地址,在写入16位的数据地址,在发送读取命令,然后连续读取6个字节,且这6个字节的数据分别是温度高位,温度低位,温度CRC校验,湿度高位,湿度低位,湿度CRC校验。

    然后我们校验完CRC正确之后就可以开始计算最终的温度了。


    QQ截图20221207103225.png

      具体的换算公式如上图所示,图中St就是16位的温度数据,Srh就是16位的湿度数据,这样就根据这个公式算出最后的温湿度了。


      经过如上分析,最终的GXHT3.c文件如下所示:
    1. #include "GXHT30.h"
    2. //基本数据:
    3. //供电范围:2.4V~5.5V,湿度误差±1.5,温度误差±0.2。
    4. //设备地址选择脚,ADDR(PIN2)接GND,设备写地址为0x88(0x44<<1),读地址为0x89(0x45<<1)。
    5. //读取的信息顺序是:温度数据(16位)+校验和(8位)+适度数据(16位)+校验和(8位)。
    6. //湿度:RH=100*(1.0*SRH / 65535),摄氏温度:T=-45+175*(1.0*ST / 65535);
    7. u16 SHT30_temperature;                              //温度数据 1位小数
    8. u16 SHT30_humidity;                                 //湿度数据 1位小数
    9. u8 read_sht_data[6];                                //数据读取缓存
    10. //========================================================================
    11. // 函数: void SHT30_Init(void)
    12. // 描述: SHT30传感器初始化
    13. // 参数:
    14. // 返回: none.
    15. // 版本: VER1.0
    16. // 日期: 2022-15-5
    17. // 备注:
    18. //========================================================================
    19. void SHT30_Init(void)
    20. {        
    21.     IIC_Writebyte(GXHT30_Slave,0x20,0x32);
    22. }
    23. //========================================================================
    24. // 函数: int  CRC8_Compute(uchar *check_data, uchar num_of_data)
    25. // 描述: CRC数据校验
    26. // 参数:
    27. // 返回: none.
    28. // 版本: VER1.0
    29. // 日期: 2022-15-5
    30. // 备注:
    31. //========================================================================
    32. int  CRC8_Compute(uchar *check_data, uchar num_of_data)
    33. {
    34.     uchar i;        // bit mask
    35.     uchar crc = 0xFF; // calculated checksum
    36.     uchar byteCtr;    // byte counter
    37.     // calculates 8-Bit checksum with given polynomial
    38.     for(byteCtr = 0; byteCtr < num_of_data; byteCtr++)
    39.     {
    40.         crc ^= (check_data[byteCtr]);//异或赋值,crc=crc^(check_data[byteCtr])
    41.         //crc校验,最高位是1就^0x31
    42.         for(i = 8; i > 0; --i)
    43.         {
    44.             if(crc & 0x80)
    45.             {
    46.                 crc = (crc << 1) ^ 0x31;
    47.             }  
    48.             else
    49.             {
    50.                 crc = (crc << 1);
    51.             }
    52.         }
    53.     }
    54.     return crc;
    55. }
    56. //========================================================================
    57. // 函数: int  CRC8_Compute(uchar *check_data, uchar num_of_data)
    58. // 描述: CRC数据校验
    59. // 参数:
    60. // 返回: none.
    61. // 版本: VER1.0
    62. // 日期: 2022-15-5
    63. // 备注:
    64. //========================================================================
    65. int SHT30_CRC8_Check(uchar *p,uchar num_of_data,uchar CrcData)
    66. {
    67.     uchar crc;
    68.     crc = CRC8_Compute(p, num_of_data);// calculates 8-Bit checksum
    69.     if(crc != CrcData)
    70.     {   
    71.         return 1;           
    72.     }
    73.     return 0;
    74. }
    75. //========================================================================
    76. // 函数: void SHT30_DataRead(void)
    77. // 描述: 温湿度数据读取
    78. // 参数:
    79. // 返回: none.
    80. // 版本: VER1.0
    81. // 日期: 2022-15-5
    82. // 备注:
    83. //========================================================================
    84. void SHT30_DataRead(void)
    85. {
    86.         uchar temporary[3];           //用于暂时存放总线读出的温湿度值
    87.     uint  Data_convert;           //用于数据转换
    88.     uchar crc_result;             //用于CRC校验结果存放,为判断数据准确性做准备
    89.         
    90.         //read_sht30(read_sht_data,6);  //读出数据放入缓存数组等待处理>>T高八位>>T低八位>>温度T的CRC校验位>>H高八位>>H低八位>>湿度H的CRC校验位
    91.         
    92.     IIC_ReadNbyte_16bitAddr(GXHT30_Slave,0XE000,read_sht_data,6);
    93.    
    94.     temporary[0]=read_sht_data[0];//温度数据高八位
    95.     temporary[1]=read_sht_data[1];//温度数据低八位
    96.     temporary[2]=read_sht_data[2];//温度数据CRC校验位
    97.         
    98.     crc_result=SHT30_CRC8_Check(temporary,2,temporary[2]);         //crc校验,crc校验要是不成功就返回1,
    99.                                                                  //同时不会更新温度值
    100.     if(crc_result==0)  
    101.     {
    102.         Data_convert=(uint)(temporary[0] << 8) | temporary[1];  //把2个8位数据拼接为一个16位的数据
    103.         //温度转换,将16位温度数据转化为10进制的温度数据,
    104.         //这里保留了一位小数,SHT30_temperature这是一个全局变量,
    105.         //计算温度值(uchar强制转换,数据在超过八位范围后会丢失)
    106.         SHT30_temperature = (uint)((175.0 * (float)(0x7fff&Data_convert) / 65535.0 - 45.0) *10.0);
    107.     }
    108.     temporary[0]=read_sht_data[3];//湿度数据高八位
    109.     temporary[1]=read_sht_data[4];//湿度数据低八位
    110.     temporary[2]=read_sht_data[5];//湿度数据CRC校验位
    111.    
    112.     //crc校验
    113.     crc_result=SHT30_CRC8_Check(temporary,2,temporary[2]);   //crc校验,crc校验要是不成功就返回1,
    114.                                                                  //同时不会更新湿度值
    115.           if(crc_result==0)
    116.     {
    117.         Data_convert=(uint)(temporary[0] << 8) | temporary[1];
    118.         //湿度转换,将16位湿度数据转化为10进制的湿度数据,
    119.         //这里保留了一位小数,SHT30_humidity这是一个全局变量,
    120.         //计算湿度值(uchar强制转换,数据在超过八位范围后会丢失)
    121.         SHT30_humidity = (uint)((100.0 * (float)(0x7fff&Data_convert) / 65535.0) *10.0);
    122.     }        
    123. }
    复制代码




      最终的GXHT3.h文件如下所示:
    1. #ifndef __GXHT30_H
    2. #define __GXHT30_H
    3. #include "iic.h"
    4. #define GXHT30_Slave 0x88       //器件的IIC地址
    5. extern u16 SHT30_temperature;
    6. extern u16 SHT30_humidity;
    7. void SHT30_Init(void);
    8. void SHT30_DataRead(void);
    9.    
    10. #endif
    复制代码



      最后改写一下main.c里的程序,最后的main.c如下所示
    1. /*---------------------------------------------------------------------*/
    2. /* --- STC MCU Limited ------------------------------------------------*/
    3. /* --- STC 1T Series MCU Demo Programme -------------------------------*/
    4. /* --- Mobile: (86)13922805190 ----------------------------------------*/
    5. /* --- Fax: 86-0513-55012956,55012947,55012969 ------------------------*/
    6. /* --- Tel: 86-0513-55012928,55012929,55012966 ------------------------*/
    7. /* --- Web: www.STCMCU.com --------------------------------------------*/
    8. /* --- Web: www.STCMCUDATA.com  ---------------------------------------*/
    9. /* --- QQ:  800003751 -------------------------------------------------*/
    10. /* 如果要在程序中使用此代码,请在程序中注明使用了STC的资料及程序        */
    11. /*---------------------------------------------------------------------*/
    12. /*************  功能说明    **************
    13. 本例程基于STC32G核心转接板(屠龙刀)进行编写测试。
    14. 使用USB线连接核心板USB接口与电脑;
    15. MCU通过USB CDC(Communication Device Class)协议识别为串口设备;
    16. 使用串口助手打开CDC虚拟串口,然后向MCU发送数据,MCU返回接收到的数据到串口助手;
    17. 数据长度限制在63字节以内。
    18. 此外程序演示两种复位进入USB下载模式的方法:
    19. 1. 通过每1毫秒执行一次“KeyResetScan”函数,实现长按P3.2口按键触发MCU复位,进入USB下载模式。
    20.    (如果不希望复位进入USB下载模式的话,可在复位代码里将 IAP_CONTR 的bit6清0,选择复位进用户程序区)
    21. 2. 通过加载“stc_usb_cdc_32g.lib”库函数,实现使用STC-ISP软件发送指令触发MCU复位,进入USB下载模式并自动下载。
    22.    (注意:使用CDC接口触发MCU复位并自动下载功能,需要勾选端口设置:下次强制使用”STC USB Writer (HID)”进行ISP下载)
    23. 下载时, 选择时钟 24MHZ (用户可自行修改频率)。
    24. ******************************************/
    25. #include "stc.h"
    26. #include "usb.h"
    27. #include "GXHT30.h"
    28. #define MAIN_Fosc       24000000L   //定义主时钟
    29. char *USER_DEVICEDESC = NULL;
    30. char *USER_PRODUCTDESC = NULL;
    31. char *USER_STCISPCMD = "@STCISP#";                      //设置自动复位到ISP区的用户接口命令
    32. //P3.2口按键复位所需变量
    33. bit Key_Flag;
    34. u16 Key_cnt;
    35. void sys_init();
    36. void delay_ms(u8 ms);
    37. void KeyResetScan(void);
    38. void main()
    39. {
    40.     u32 count = 0;
    41.     sys_init();  //系统初始化
    42.     usb_init();  //USB CDC 接口配置
    43.    
    44.     IIC_Init();
    45.     SHT30_Init();
    46.     EA = 1;
    47.    
    48.     while (1)
    49.     {
    50.         delay_ms(1);
    51.         KeyResetScan();   //长按P3.2口按键触发软件复位,进入USB下载模式,不需要此功能可删除本行代码
    52.         if(DeviceState != DEVSTATE_CONFIGURED)  //等待USB完成配置
    53.             continue;
    54.         if (bUsbOutReady)
    55.         {
    56.             usb_OUT_done();    //接收应答(固定格式)
    57.             
    58.             printf("OutNumber=0x%X\r\n",OutNumber);  //使用 printf 函数打印接收数据长度
    59.             memcpy(UsbInBuffer, UsbOutBuffer, OutNumber);  //将接收数据(UsbOutBuffer),复制到发送缓冲区(UsbInBuffer)
    60.             usb_IN(OutNumber);      //原路返回, 用于测试
    61.         }
    62.         delay_ms(250);delay_ms(250);
    63.         SHT30_DataRead();
    64.         printf("温度:%.1f℃\t湿度:%.1f%%\r\n",(float)SHT30_temperature/10,(float)SHT30_humidity/10);
    65.     }
    66. }
    67. void sys_init()
    68. {
    69.     WTST = 0;  //设置程序指令延时参数,赋值为0可将CPU执行指令的速度设置为最快
    70.     EAXFR = 1; //扩展寄存器(XFR)访问使能
    71.     CKCON = 0; //提高访问XRAM速度
    72.     P0M1 = 0x00;   P0M0 = 0x00;   //设置为准双向口
    73.     P1M1 = 0x00;   P1M0 = 0x00;   //设置为准双向口
    74.     P2M1 = 0x00;   P2M0 = 0x00;   //设置为准双向口
    75.     P3M1 = 0x00;   P3M0 = 0x00;   //设置为准双向口
    76.     P4M1 = 0x00;   P4M0 = 0x00;   //设置为准双向口
    77.     P5M1 = 0x00;   P5M0 = 0x00;   //设置为准双向口
    78.     P6M1 = 0x00;   P6M0 = 0x00;   //设置为准双向口
    79.     P7M1 = 0x00;   P7M0 = 0x00;   //设置为准双向口
    80.     //====== USB 初始化 ======
    81.     P3M0 &= ~0x03;
    82.     P3M1 |= 0x03;
    83.    
    84.     IRC48MCR = 0x80;
    85.     while (!(IRC48MCR & 0x01));
    86.    
    87.     USBCLK = 0x00;
    88.     USBCON = 0x90;
    89.     //========================
    90. }
    91. //========================================================================
    92. // 函数: void delay_ms(u8 ms)
    93. // 描述: 延时函数。
    94. // 参数: ms,要延时的ms数, 这里只支持1~255ms. 自动适应主时钟.
    95. // 返回: none.
    96. // 版本: VER1.0
    97. // 日期: 2021-3-9
    98. // 备注:
    99. //========================================================================
    100. void delay_ms(u8 ms)
    101. {
    102.      u16 i;
    103.      do{
    104.           i = MAIN_Fosc / 6000;
    105.           while(--i);   //6T per loop
    106.      }while(--ms);
    107. }
    108. //========================================================================
    109. // 函数: void KeyResetScan(void)
    110. // 描述: P3.2口按键长按1秒触发软件复位,进入USB下载模式。
    111. // 参数: none.
    112. // 返回: none.
    113. // 版本: VER1.0
    114. // 日期: 2022-6-11
    115. // 备注:
    116. //========================================================================
    117. void KeyResetScan(void)
    118. {
    119.     if(!P32)
    120.     {
    121.         if(!Key_Flag)
    122.         {
    123.             Key_cnt++;
    124.             if(Key_cnt >= 1000)                //连续1000ms有效按键检测
    125.             {
    126.                 Key_Flag = 1;                //设置按键状态,防止重复触发
    127.                 USBCON = 0x00;      //清除USB设置
    128.                 USBCLK = 0x00;
    129.                 IRC48MCR = 0x00;
    130.                
    131.                 delay_ms(10);
    132.                 IAP_CONTR = 0x60;   //触发软件复位,从ISP开始执行
    133.                 while (1);
    134.             }
    135.         }
    136.     }
    137.     else
    138.     {
    139.         Key_cnt = 0;
    140.         Key_Flag = 0;
    141.     }
    142. }
    复制代码

      这段代码主要就是在while函数里添加了温湿度读取的指令,然后通过现成的PRINTF函数给他直接打印了出来,这里需要注意一下因为我们采样率设置的0.5mps,所以我们需要间隔500ms以上读取一次,这里为了方便理解直接两个250ms的delay实现了,移植到自己程序的时候可以加个delay。最终实现的效果如下,当然也能看帖子最前面的GIF。


    QQ截图20221207104805.png

      这里加上一个接线图连接示意图哈!
    MCU(STC32G)
    GXHT30
    5V
    VCC
    GND
    GND
    P2.4
    SDA
    P2.5
    SCL
      

    看完了文章,觉得有用的请在文末点个赞哈哈;P由于附件的空间受限,需要上述所需文件的请进群下载~




    回复 送花

    使用道具 举报

  • TA的每日心情
    开心
    昨天 10:10
  • 签到天数: 47 天

    [LV.5]常住居民I

    26

    主题

    784

    回帖

    5843

    积分

    荣誉版主

    冲哥视频教程和各种开源资料QQ交流群884047237,可群

    积分
    5843
    QQ
     楼主| 发表于 2022-12-7 13:35:24 | 显示全部楼层
    本帖最后由 STC爱好者 于 2023-1-14 10:45 编辑

    以后的传感器系列的教程都会用USB-CDC虚拟串口传到出口助手上显示数据,这样可以帮大家方便快捷的查看数据,且波特率可以任意选择~!移植的时候也只需要把打印函数里的变量挪到自己的用户函数里做显示或者计算就好了。

    该用户从未签到

    505

    主题

    7699

    回帖

    1万

    积分

    管理员

    积分
    14074
    发表于 2022-12-7 13:59:16 | 显示全部楼层
    估计: STC64 + 冲哥传感器拓展 = 人工智能
  • TA的每日心情
    无聊
    昨天 01:03
  • 签到天数: 88 天

    [LV.6]常住居民II

    2

    主题

    60

    回帖

    2476

    积分

    金牌会员

    积分
    2476
    发表于 2022-12-7 14:31:02 | 显示全部楼层
    学习ing,感谢冲哥的无私奉献

    该用户从未签到

    0

    主题

    1

    回帖

    10

    积分

    新手上路

    积分
    10
    发表于 2023-1-13 10:47:29 | 显示全部楼层
    新人刚进论坛,请问QQ群号是多少,亟盼加入组织,感谢!
  • TA的每日心情
    开心
    昨天 10:10
  • 签到天数: 47 天

    [LV.5]常住居民I

    26

    主题

    784

    回帖

    5843

    积分

    荣誉版主

    冲哥视频教程和各种开源资料QQ交流群884047237,可群

    积分
    5843
    QQ
     楼主| 发表于 2023-1-13 11:28:18 | 显示全部楼层
    sealinkiot 发表于 2023-1-13 10:47
    新人刚进论坛,请问QQ群号是多少,亟盼加入组织,感谢!

    Q群:884047237。现在人不多:lol可以有时间一个个回复:lol

    该用户从未签到

    40

    主题

    2636

    回帖

    5833

    积分

    超级版主

    积分
    5833
    发表于 2023-1-13 11:37:11 | 显示全部楼层
    温湿度传感器我用的DHT22(AM2302),出厂校准好,温湿度直读,不需要计算。

    点评

    这不错。赞一个  发表于 2023-1-13 11:42
  • TA的每日心情
    开心
    昨天 22:14
  • 签到天数: 66 天

    [LV.6]常住居民II

    21

    主题

    228

    回帖

    639

    积分

    荣誉版主

    Cyber Hamster

    积分
    639
    发表于 2023-1-16 19:59:22 来自手机 | 显示全部楼层
    sensor

    点评

    英文不好,下次改正  发表于 2023-1-16 20:59
    回复 送花

    使用道具 举报

    该用户从未签到

    6

    主题

    11

    回帖

    146

    积分

    注册会员

    积分
    146
    发表于 2023-2-6 19:16:25 | 显示全部楼层
    SendData(slave); //发送设备地址+写命令,这个为什么是写命令没有明白,不是这个只有发送从机设备地址吗?

    点评

    楼主可以先了解一下I2C通讯协议。  详情 回复 发表于 2023-4-14 09:54
  • TA的每日心情
    开心
    昨天 10:10
  • 签到天数: 47 天

    [LV.5]常住居民I

    26

    主题

    784

    回帖

    5843

    积分

    荣誉版主

    冲哥视频教程和各种开源资料QQ交流群884047237,可群

    积分
    5843
    QQ
     楼主| 发表于 2023-2-6 21:47:41 | 显示全部楼层
    fgx 发表于 2023-2-6 19:16
    SendData(slave); //发送设备地址+写命令,这个为什么是写命令没有明白,不是这个只有发送从机设备地址吗 ...

    1.png


    读写控制位,读操作为1,写操作为0。
    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|深圳国芯人工智能有限公司 ( 粤ICP备2022108929号-2 )

    GMT+8, 2024-2-24 01:52 , Processed in 0.085849 second(s), 70 queries .

    Powered by Discuz! X3.5

    © 2001-2024 Discuz! Team.

    快速回复 返回顶部 返回列表