打印
[其他ST产品]

嵌入式STM32深入之基于IIC和SPI协议的温湿度采集

[复制链接]
271|28
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
接下来我们将介绍深入的第一个实验就是使用外设(外部传感器)进行利用简单的温度传感器采集温度,以及利用LED显示屏进行显示。具体实现进行一下实验:

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

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


使用特权

评论回复
评论
裤脚口感好 2023-1-31 21:16 回复TA
———————————————— 版权声明:本文为CSDN博主「笑着前进.h」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/huang_527/article/details/127650840 
沙发
裤脚口感好|  楼主 | 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 | 只看该作者

所有接到I2C总线设备上的串行数据SDA都接到总线的SDA上,各设备的时钟线SCL接到总线的SCL上。I2C总线上的每个设备都自己一个唯一的地址,来确保不同设备之间访问的准确性。

使用特权

评论回复
5
裤脚口感好|  楼主 | 2023-1-31 21:16 | 只看该作者
I2C协议层
I2C 总线在传送数据过程中共有三种类型信号, 它们分别是:开始信号、结束信号和应答信号。

开始信号SCL 为高电平时,SDA 由高电平向低电平跳变,开始传送数据。
结束信号SCL 为高电平时,SDA 由低电平向高电平跳变,结束传送数据。
应答信号接收数据的 IC 在接收到 8bit 数据后,向发送数据的 IC 发出特定的低电平脉冲,表示已收到数据。CPU 向受控单元发出一个信号后,等待受控单元发出一个应答信号,CPU 接收到应答信号后,根据实际情况作出是否继续传递信号的判断。若未收到应答信号,由判断为受控单元出现故障。

使用特权

评论回复
6
裤脚口感好|  楼主 | 2023-1-31 21:17 | 只看该作者
I2C总线时序图


使用特权

评论回复
7
裤脚口感好|  楼主 | 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由于不受管脚限制,接口比较灵活。

使用特权

评论回复
8
裤脚口感好|  楼主 | 2023-1-31 21:18 | 只看该作者
STM32基于I2C协议的温湿度传感器的数据采集
1、题目要求
每隔2秒钟采集一次温湿度数据,并通过串口发送到上位机,利用串口显示温度与湿度。

使用特权

评论回复
9
裤脚口感好|  楼主 | 2023-1-31 21:18 | 只看该作者
前期准备
物理设备:

温湿度传感器AHT20(如图)
串口调试助手
面包板、杜邦线等

使用特权

评论回复
10
裤脚口感好|  楼主 | 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

使用特权

评论回复
11
裤脚口感好|  楼主 | 2023-1-31 21:20 | 只看该作者
代码撰写
(1)之前的空的工程文件中,mian.c 函数写入:

#include "delay.h"
#include "usart.h"
#include "bsp_i2c.h"


int main(void)
{       
        delay_init();      
        uart_init(115200);         
        IIC_Init();
                while(1)
        {
                printf("此时测量温度/湿度显示:");
                printf("\r\n");
                read_AHT20_once();
                delay_ms(1500);
  }
}

使用特权

评论回复
12
裤脚口感好|  楼主 | 2023-1-31 21:21 | 只看该作者

使用特权

评论回复
13
裤脚口感好|  楼主 | 2023-1-31 21:21 | 只看该作者
然后把以下代码文件和main.c放到一起——usart.c
#include "sys.h"
#include "usart.h"


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


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

//           


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

};

FILE __stdout;      
//¶¨Òå_sys_exit()ÒÔ±ÜÃâʹÓðëÖ÷»úģʽ   
void _sys_exit(int x)
{
        x = x;
}
//Öض¨Òåfputcº¯Êý
int fputc(int ch, FILE *f)
{      
        while((USART1->SR&0X40)==0);//Ñ­»··¢ËÍ,Ö±µ½·¢ËÍÍê±Ï   
    USART1->DR = (u8) ch;      
        return ch;
}
#endif

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

        while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET) {}       
   
    return ch;
}
int GetKey (void)  {

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

    return ((int)(USART1->DR & 0x1FF));
}
*/

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

   //Usart1 NVIC ÅäÖÃ

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

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

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

}



void USART1_IRQHandler(void)                        //´®¿Ú1ÖжϷþÎñ³ÌÐò
        {
        u8 Res;
#ifdef OS_TICKS_PER_SEC                 //Èç¹ûʱÖÓ½ÚÅÄÊý¶¨ÒåÁË,˵Ã÷ҪʹÓÃucosIIÁË.
        OSIntEnter();   
#endif
        if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)  //½ÓÊÕÖжÏ(½ÓÊÕµ½µÄÊý¾Ý±ØÐëÊÇ0x0d 0x0a½áβ)
                {
                Res =USART_ReceiveData(USART1);//(USART1->DR);        //¶ÁÈ¡½ÓÊÕµ½µÄÊý¾Ý
               
                if((USART_RX_STA&0x8000)==0)//½ÓÊÕδÍê³É
                        {
                        if(USART_RX_STA&0x4000)//½ÓÊÕµ½ÁË0x0d
                                {
                                if(Res!=0x0a)USART_RX_STA=0;//½ÓÊÕ´íÎó,ÖØпªÊ¼
                                else USART_RX_STA|=0x8000;        //½ÓÊÕÍê³ÉÁË
                                }
                        else //»¹Ã»ÊÕµ½0X0D
                                {       
                                if(Res==0x0d)USART_RX_STA|=0x4000;
                                else
                                        {
                                        USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ;
                                        USART_RX_STA++;
                                        if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//½ÓÊÕÊý¾Ý´íÎó,ÖØпªÊ¼½ÓÊÕ          
                                        }                 
                                }
                        }                    
     }
#ifdef OS_TICKS_PER_SEC                 //Èç¹ûʱÖÓ½ÚÅÄÊý¶¨ÒåÁË,˵Ã÷ҪʹÓÃucosIIÁË.
        OSIntExit();                                                                                           
#endif
}
#endif       


使用特权

评论回复
14
裤脚口感好|  楼主 | 2023-1-31 21:21 | 只看该作者

使用特权

评论回复
15
裤脚口感好|  楼主 | 2023-1-31 21:21 | 只看该作者
usart.h
#ifndef __USART_H
#define __USART_H
#include "stdio.h"       
#include "sys.h"

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

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

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


使用特权

评论回复
16
裤脚口感好|  楼主 | 2023-1-31 22:38 | 只看该作者

使用特权

评论回复
17
裤脚口感好|  楼主 | 2023-1-31 22:38 | 只看该作者
注:本来打算把代码全部都写完的,然后直接演示给大家,但是我将bsp_i2c.c文件放进去后发现根本不够,可以看到有500+行,所以大家就下载官方模板,看着上面前期准备那里的结果来添加,这里我就简单描述重要的代码即可。

使用特权

评论回复
18
裤脚口感好|  楼主 | 2023-1-31 22:38 | 只看该作者

使用特权

评论回复
19
裤脚口感好|  楼主 | 2023-1-31 22:38 | 只看该作者
AHT20芯片的使用过程 read_AHT20_once函数
void  read_AHT20_once(void)
{
        delay_ms(10);

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

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

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

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


使用特权

评论回复
20
裤脚口感好|  楼主 | 2023-1-31 22:40 | 只看该作者

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

32

主题

238

帖子

0

粉丝