[其他ST产品] 嵌入式STM32深入之基于IIC和SPI协议的温湿度采集

[复制链接]
1403|28
 楼主| 裤脚口感好 发表于 2023-1-31 21:10 | 显示全部楼层 |阅读模式
接下来我们将介绍深入的第一个实验就是使用外设(外部传感器)进行利用简单的温度传感器采集温度,以及利用LED显示屏进行显示。具体实现进行一下实验:

- 使用STM32F103完成基于I2C协议的AHT20温湿度传感器的数据采集,并将采集的温度-湿度值通过串口输出。

二、I2C总线通信协议
1、I2C介绍
IIC(Inter-Integrated Circuit)总线是一种由NXP(原PHILIPS)公司开发的两线式串行总线,用于连接微控制器及其外围设备。多用于主控制器和从器件间的主从通信,在小数据量场合使用,传输距离短,任意时刻只能有一个主机等特性。
在CPU与被控IC之间、IC与IC之间进行双向传送,高速IIC总线一般可达400kbps 以上。
注意IIC是为了与低速设备通信而发明的,所以IIC的传输速率比不上SPI
2735063d9135e7346d.png

评论

———————————————— 版权声明:本文为CSDN博主「笑着前进.h」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/huang_527/article/details/127650840  发表于 2023-1-31 21:16
 楼主| 裤脚口感好 发表于 2023-1-31 21:11 | 显示全部楼层
I²C最重要的功能包括:

只需要两条总线;
没有严格的波特率要求,例如使用RS232,主设备生成总线时钟;
所有组件之间都存在简单的主/从关系,连接到总线的每个设备均可通过唯一地址进行软件寻址;
I²C是真正的多主设备总线,可提供仲裁和冲突检测;
传输速度;
(1)标准模式:Standard Mode = 100 Kbps
(2)快速模式:Fast Mode = 400 Kbps
(3)高速模式: High speed mode = 3.4 Mbps
(4)超快速模式: Ultra fast mode = 5 Mbps
最大主设备数:无限制;
最大从机数:理论上是127。
 楼主| 裤脚口感好 发表于 2023-1-31 21:14 | 显示全部楼层
I2C的物理层
I2C 总线在物理连接上非常简单,分别由SDA(串行数据线)和SCL(串行时钟线)及上拉电阻组成。通信原理是通过对SCL和SDA线高低电平时序的控制,来产生I2C总线协议所需要的信号进行数据的传递。在总线空闲状态时,SCL和SDA被上拉电阻Rp拉高,使SDA和SCL线都保持高电平。

SDA(Serial data)是数据线,D代表Data也就是数据,Send Data 也就是用来传输数据的
SCL(Serial clock line)是时钟线,C代表Clock 也就是时钟 也就是控制数据发送的时序的

I2C通信方式为半双工,只有一根SDA线,同一时间只可以单向通信,485也为半双工,SPI和uart通信为全双工。

 楼主| 裤脚口感好 发表于 2023-1-31 21:15 | 显示全部楼层
1468163d91451f165f.png
所有接到I2C总线设备上的串行数据SDA都接到总线的SDA上,各设备的时钟线SCL接到总线的SCL上。I2C总线上的每个设备都自己一个唯一的地址,来确保不同设备之间访问的准确性。
8950763d91464a0170.png
 楼主| 裤脚口感好 发表于 2023-1-31 21:16 | 显示全部楼层
I2C协议层
I2C 总线在传送数据过程中共有三种类型信号, 它们分别是:开始信号、结束信号和应答信号。

开始信号SCL 为高电平时,SDA 由高电平向低电平跳变,开始传送数据。
结束信号SCL 为高电平时,SDA 由低电平向高电平跳变,结束传送数据。
应答信号接收数据的 IC 在接收到 8bit 数据后,向发送数据的 IC 发出特定的低电平脉冲,表示已收到数据。CPU 向受控单元发出一个信号后,等待受控单元发出一个应答信号,CPU 接收到应答信号后,根据实际情况作出是否继续传递信号的判断。若未收到应答信号,由判断为受控单元出现故障。
 楼主| 裤脚口感好 发表于 2023-1-31 21:17 | 显示全部楼层
I2C总线时序图

6fdeee0c666a45959f19a064e83e0075.png


 楼主| 裤脚口感好 发表于 2023-1-31 21:18 | 显示全部楼层
软件IIC和硬件IIC
IIC分为软件IIC和硬件IIC。软件IIC 软件IIC通信指的是用单片机的两个I/O端口模拟出来的IIC,用软件控制管脚状态以模拟I2C通信波形,软件模拟寄存器的工作方式。

直接使用 CPU 内核按照 I2C 协议的要求控制 GPIO 输出高低电平,从而模拟I2C。
使用: 需要在控制产生 I2C 的起始信号时,控制作为SCL 线的 GPIO 引脚输出高电平,然后控制作为 SDA 线的 GPIO 引脚在此期间完成由高电平至低电平的切换,最后再控制SCL线切换为低电平,这样就输出了一个标准的 I2C 起始信号。

硬件IIC:一块硬件电路,硬件I2C对应芯片上的I2C外设,有相应I2C驱动电路,其所使用的I2C管脚也是专用的,硬件(固件)I2C是直接调用内部寄存器进行配置。

直接利用 STM32 芯片中的硬件 I2C 外设。
使用: 只要配置好对应的寄存器,外设就会产生标准串口协议的时序。在初始化好 I2C 外设后,只需要把某寄存器位置 1,此时外设就会控制对应的 SCL 及 SDA 线自动产生 I2C 起始信号,不需要内核直接控制引脚的电平。

硬件I2C的效率要远高于软件的,而软件I2C由于不受管脚限制,接口比较灵活。
 楼主| 裤脚口感好 发表于 2023-1-31 21:18 | 显示全部楼层
STM32基于I2C协议的温湿度传感器的数据采集
1、题目要求
每隔2秒钟采集一次温湿度数据,并通过串口发送到上位机,利用串口显示温度与湿度。
 楼主| 裤脚口感好 发表于 2023-1-31 21:18 | 显示全部楼层
前期准备
物理设备:

温湿度传感器AHT20(如图)
串口调试助手
面包板、杜邦线等
9005963d91527d9409.png
 楼主| 裤脚口感好 发表于 2023-1-31 21:20 | 显示全部楼层
(1)了解AHT20芯片的相关信息,具体信息请到官方下载对应产品介绍文档,资料链接如下
http://www.aosong.com/class-36.html
(2)具体代码添加过程
在野火提供的示例代码中,打开一个只包含固件库的空项目。向工程中添加相关代码,添加代码的具体内容请参考下面链接:
https://blog.csdn.net/hhhhhh277523/article/details/111397514
(3)本次完整实验代码参考:
https://pan.baidu.com/s/1z1SybDqb_6V9veqxqLs7sQ
 楼主| 裤脚口感好 发表于 2023-1-31 21:20 | 显示全部楼层
代码撰写
(1)之前的空的工程文件中,mian.c 函数写入:

  1. #include "delay.h"
  2. #include "usart.h"
  3. #include "bsp_i2c.h"


  4. int main(void)
  5. {       
  6.         delay_init();      
  7.         uart_init(115200);         
  8.         IIC_Init();
  9.                 while(1)
  10.         {
  11.                 printf("此时测量温度/湿度显示:");
  12.                 printf("\r\n");
  13.                 read_AHT20_once();
  14.                 delay_ms(1500);
  15.   }
  16. }

 楼主| 裤脚口感好 发表于 2023-1-31 21:21 | 显示全部楼层
 楼主| 裤脚口感好 发表于 2023-1-31 21:21 | 显示全部楼层
然后把以下代码文件和main.c放到一起——usart.c
  1. #include "sys.h"
  2. #include "usart.h"


  3. //STM32F103ºËÐİåÀý³Ì
  4. //¿âº¯Êý°æ±¾Àý³Ì
  5. /********** mcudev.taobao.com ³öÆ·  ********/


  6. //          
  7. //Èç¹ûʹÓÃucos,Ôò°üÀ¨ÏÂÃæµÄÍ·Îļþ¼´¿É.
  8. #if SYSTEM_SUPPORT_UCOS
  9. #include "includes.h"                                        //ucos ʹÓà         
  10. #endif
  11. //         
  12. //STM32¿ª·¢°å
  13. //´®¿Ú1³õʼ»¯                  

  14. //           


  15. //
  16. //¼ÓÈëÒÔÏ´úÂë,Ö§³Öprintfº¯Êý,¶ø²»ÐèҪѡÔñuse MicroLIB          
  17. #if 1
  18. #pragma import(__use_no_semihosting)            
  19. //±ê×¼¿âÐèÒªµÄÖ§³Öº¯Êý                 
  20. struct __FILE
  21. {
  22.         int handle;

  23. };

  24. FILE __stdout;      
  25. //¶¨Òå_sys_exit()ÒÔ±ÜÃâʹÓðëÖ÷»úģʽ   
  26. void _sys_exit(int x)
  27. {
  28.         x = x;
  29. }
  30. //ÖØ¶¨Òåfputcº¯Êý
  31. int fputc(int ch, FILE *f)
  32. {      
  33.         while((USART1->SR&0X40)==0);//Ñ­»··¢ËÍ,Ö±µ½·¢ËÍÍê±Ï   
  34.     USART1->DR = (u8) ch;      
  35.         return ch;
  36. }
  37. #endif

  38. /*ʹÓÃmicroLibµÄ·½·¨*/
  39. /*
  40. int fputc(int ch, FILE *f)
  41. {
  42.         USART_SendData(USART1, (uint8_t) ch);

  43.         while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET) {}       
  44.    
  45.     return ch;
  46. }
  47. int GetKey (void)  {

  48.     while (!(USART1->SR & USART_FLAG_RXNE));

  49.     return ((int)(USART1->DR & 0x1FF));
  50. }
  51. */

  52. #if EN_USART1_RX   //Èç¹ûʹÄÜÁ˽ÓÊÕ
  53. //´®¿Ú1ÖжϷþÎñ³ÌÐò
  54. //×¢Òâ,¶ÁÈ¡USARTx->SRÄܱÜÃâĪÃûÆäÃîµÄ´íÎó          
  55. u8 USART_RX_BUF[USART_REC_LEN];     //½ÓÊÕ»º³å,×î´óUSART_REC_LEN¸ö×Ö½Ú.
  56. //½ÓÊÕ״̬
  57. //bit15£¬        ½ÓÊÕÍê³É±êÖ¾
  58. //bit14£¬        ½ÓÊÕµ½0x0d
  59. //bit13~0£¬        ½ÓÊÕµ½µÄÓÐЧ×Ö½ÚÊýÄ¿
  60. u16 USART_RX_STA=0;       //½ÓÊÕ״̬±ê¼Ç          
  61.   
  62. void uart_init(u32 bound){
  63.     //GPIO¶Ë¿ÚÉèÖÃ
  64.   GPIO_InitTypeDef GPIO_InitStructure;
  65.         USART_InitTypeDef USART_InitStructure;
  66.         NVIC_InitTypeDef NVIC_InitStructure;
  67.          
  68.         RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE);        //ʹÄÜUSART1£¬GPIOAʱÖÓ
  69.      //USART1_TX   PA.9
  70.     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9
  71.     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  72.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;        //¸´ÓÃÍÆÍìÊä³ö
  73.     GPIO_Init(GPIOA, &GPIO_InitStructure);
  74.    
  75.     //USART1_RX          PA.10
  76.     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
  77.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//¸¡¿ÕÊäÈë
  78.     GPIO_Init(GPIOA, &GPIO_InitStructure);  

  79.    //Usart1 NVIC ÅäÖÃ

  80.     NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
  81.         NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//ÇÀÕ¼ÓÅÏȼ¶3
  82.         NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;                //×ÓÓÅÏȼ¶3
  83.         NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;                        //IRQͨµÀʹÄÜ
  84.         NVIC_Init(&NVIC_InitStructure);        //¸ù¾ÝÖ¸¶¨µÄ²ÎÊý³õʼ»¯VIC¼Ä´æÆ÷
  85.   
  86.    //USART ³õʼ»¯ÉèÖÃ

  87.         USART_InitStructure.USART_BaudRate = bound;//Ò»°ãÉèÖÃΪ9600;
  88.         USART_InitStructure.USART_WordLength = USART_WordLength_8b;//×Ö³¤Îª8λÊý¾Ý¸ñʽ
  89.         USART_InitStructure.USART_StopBits = USART_StopBits_1;//Ò»¸öֹͣλ
  90.         USART_InitStructure.USART_Parity = USART_Parity_No;//ÎÞÆæÅ¼Ð£Ñéλ
  91.         USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//ÎÞÓ²¼þÊý¾ÝÁ÷¿ØÖÆ
  92.         USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;        //ÊÕ·¢Ä£Ê½

  93.     USART_Init(USART1, &USART_InitStructure); //³õʼ»¯´®¿Ú
  94.     USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//¿ªÆôÖжÏ
  95.     USART_Cmd(USART1, ENABLE);                    //ʹÄÜ´®¿Ú

  96. }



  97. void USART1_IRQHandler(void)                        //´®¿Ú1ÖжϷþÎñ³ÌÐò
  98.         {
  99.         u8 Res;
  100. #ifdef OS_TICKS_PER_SEC                 //Èç¹ûʱÖÓ½ÚÅÄÊý¶¨ÒåÁË,˵Ã÷ҪʹÓÃucosIIÁË.
  101.         OSIntEnter();   
  102. #endif
  103.         if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)  //½ÓÊÕÖжÏ(½ÓÊÕµ½µÄÊý¾Ý±ØÐëÊÇ0x0d 0x0a½áβ)
  104.                 {
  105.                 Res =USART_ReceiveData(USART1);//(USART1->DR);        //¶ÁÈ¡½ÓÊÕµ½µÄÊý¾Ý
  106.                
  107.                 if((USART_RX_STA&0x8000)==0)//½ÓÊÕδÍê³É
  108.                         {
  109.                         if(USART_RX_STA&0x4000)//½ÓÊÕµ½ÁË0x0d
  110.                                 {
  111.                                 if(Res!=0x0a)USART_RX_STA=0;//½ÓÊÕ´íÎó,ÖØÐ¿ªÊ¼
  112.                                 else USART_RX_STA|=0x8000;        //½ÓÊÕÍê³ÉÁË
  113.                                 }
  114.                         else //»¹Ã»ÊÕµ½0X0D
  115.                                 {       
  116.                                 if(Res==0x0d)USART_RX_STA|=0x4000;
  117.                                 else
  118.                                         {
  119.                                         USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ;
  120.                                         USART_RX_STA++;
  121.                                         if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//½ÓÊÕÊý¾Ý´íÎó,ÖØÐ¿ªÊ¼½ÓÊÕ          
  122.                                         }                 
  123.                                 }
  124.                         }                    
  125.      }
  126. #ifdef OS_TICKS_PER_SEC                 //Èç¹ûʱÖÓ½ÚÅÄÊý¶¨ÒåÁË,˵Ã÷ҪʹÓÃucosIIÁË.
  127.         OSIntExit();                                                                                           
  128. #endif
  129. }
  130. #endif       


 楼主| 裤脚口感好 发表于 2023-1-31 21:21 | 显示全部楼层
 楼主| 裤脚口感好 发表于 2023-1-31 21:21 | 显示全部楼层
usart.h
  1. #ifndef __USART_H
  2. #define __USART_H
  3. #include "stdio.h"       
  4. #include "sys.h"

  5. //STM32F103ºËÐİåÀý³Ì
  6. //¿âº¯Êý°æ±¾Àý³Ì
  7. /********** mcudev.taobao.com ³öÆ·  ********/

  8. //         
  9. //STM32¿ª·¢°å
  10. //´®¿Ú1³õʼ»¯                  

  11. #define USART_REC_LEN                          200          //¶¨Òå×î´ó½ÓÊÕ×Ö½ÚÊý 200
  12. #define EN_USART1_RX                         1                    //ʹÄÜ£¨1£©/½ûÖ¹£¨0£©´®¿Ú1½ÓÊÕ
  13.                  
  14. extern u8  USART_RX_BUF[USART_REC_LEN]; //½ÓÊÕ»º³å,×î´óUSART_REC_LEN¸ö×Ö½Ú.Ä©×Ö½ÚΪ»»Ðзû
  15. extern u16 USART_RX_STA;                         //½ÓÊÕ״̬±ê¼Ç       
  16. //Èç¹ûÏë´®¿ÚÖжϽÓÊÕ£¬Ç벻ҪעÊÍÒÔϺ궨Òå
  17. void uart_init(u32 bound);
  18. #endif


 楼主| 裤脚口感好 发表于 2023-1-31 22:38 | 显示全部楼层
 楼主| 裤脚口感好 发表于 2023-1-31 22:38 | 显示全部楼层
注:本来打算把代码全部都写完的,然后直接演示给大家,但是我将bsp_i2c.c文件放进去后发现根本不够,可以看到有500+行,所以大家就下载官方模板,看着上面前期准备那里的结果来添加,这里我就简单描述重要的代码即可。
 楼主| 裤脚口感好 发表于 2023-1-31 22:38 | 显示全部楼层
 楼主| 裤脚口感好 发表于 2023-1-31 22:38 | 显示全部楼层
AHT20芯片的使用过程 read_AHT20_once函数
  1. void  read_AHT20_once(void)
  2. {
  3.         delay_ms(10);

  4.         reset_AHT20();//重置AHT20芯片
  5.         delay_ms(10);

  6.         init_AHT20();//初始化AHT20芯片
  7.         delay_ms(10);

  8.         startMeasure_AHT20();//开始测试AHT20芯片
  9.         delay_ms(80);

  10.         read_AHT20();//读取AHT20采集的到的数据
  11.         delay_ms(5);
  12. }


 楼主| 裤脚口感好 发表于 2023-1-31 22:40 | 显示全部楼层
您需要登录后才可以回帖 登录 | 注册

本版积分规则

48

主题

340

帖子

0

粉丝
快速回复 在线客服 返回列表 返回顶部