【嵌入式学习】ARM day04.17

一、主要逻辑

写入数据:

  • 主机发送起始信号
  • 主机发送8bit从机地址+1bit写标志(0)
  • 从机回应应答信号
  • 主机发送8bit从机的寄存器地址
  • 从机回应应答信号
  • 主机发送8bit数据
  • 从机回应应答
  • 主机发送8bit数据
  • 从机回应应答
  • 主机发起终止信号

读取数据:

  • 主机发起起始信号
  • 主机发送7bit从机地址+1bit写标志
  • 从机回应应答信号
  • 主机发起一个重复的起始信号
  • 主机发送7bit从机地址+1bit读标志
  • 从机回应应答信号
  • 从机发送8bit数据
  • 主机回应应答信号
  • 从机发送8bit数据
  • 主机回应非应答信号
  • 主机发起终止信号

二、实现温湿度传感器代码

main.c

复制代码
#include "si7006.h"

int main()

{

    //si7006初始化

	i2c_init();

    si7006_init();



    unsigned short hum;

    short tem;

    while(1)

    {

        //读取温度和湿度

        hum=si7006_read_hum();

        tem=si7006_read_tem();

        //计算温湿度数据

        hum=(hum*125)/65536-6;

        tem=(tem*175.72)/65536-46.85;

        printf("hum:%d\n",hum);

        printf("tem:%d\n",tem);

        delay_ms(1000);

    }

    return 0;

}

iic.c

复制代码
#include "iic.h"

extern void printf(const char* fmt, ...);
/*
 * 函数名 : delay_us
 * 函数功能:延时函数
 * 函数参数:无
 * 函数返回值:无
 * */
void delay_us(void)
{
    unsigned int i = 2000;
    while(i--);
}
/*
 * 函数名 : i2c_init
 * 函数功能: i2C总线引脚的初始化, 通用输出,推挽输出,输出速度,
 * 函数参数:无
 * 函数返回值:无
 * */
void i2c_init(void)
{
    // 使能GPIOF端口的时钟
    RCC->MP_AHB4ENSETR |= (0x1 << 5);
    // 设置PF14,PF15引脚为通用的输出功能
    GPIOF->MODER &= (~(0xF << 28));
    GPIOF->MODER |= (0x5 << 28);
    // 设置PF14, PF15引脚为推挽输出
    GPIOF->OTYPER &= (~(0x3 << 14));
    // 设置PF14, PF15引脚为高速输出
    GPIOF->OSPEEDR |= (0xF << 28);
    // 设置PF14, PF15引脚的禁止上拉和下拉
    GPIOF->PUPDR &= (~(0xF << 28));
    // 空闲状态SDA和SCL拉高 
    I2C_SCL_H;
    I2C_SDA_H;
}



/*
 * 函数名:i2c_start
 * 函数功能:模拟i2c开始信号的时序
 * 函数参数:无
 * 函数返回值:无
 * */
void i2c_start(void)
{
    /*
     * 开始信号:时钟在高电平期间,数据线从高到低的变化
     *     --------
     * SCL         \
     *              --------
     *     ----
     * SDA     \
     *          --------
     * */   
    //确保SDA是输出状态 PF15输出
    SET_SDA_OUT;
    // 空闲状态SDA和SCL拉高 
    I2C_SCL_H;
    I2C_SDA_H;
    delay_us();//延时等待一段时间
    I2C_SDA_L;//数据线拉低
    delay_us();//延时等待一段时间
    I2C_SCL_L;//时钟线拉低,让总线处于占用状态
}

/*
 * 函数名:i2c_stop
 * 函数功能:模拟i2c停止信号的时序
 * 函数参数:无
 * 函数返回值:无
 * */

void i2c_stop(void)
{
    /*
     * 停止信号 : 时钟在高电平期间,数据线从低到高的变化 
     *             ----------
     * SCL        /
     *    --------
     *    ---         -------
     * SDA   X       /
     *    --- -------
     * */
    //确保SDA是输出状态 PF15输出
    SET_SDA_OUT;
    //时钟线拉低
    I2C_SCL_L;
    delay_us();//延时等待一段时间
    I2C_SDA_L;//数据线拉低
    delay_us();//延时等待一段时间
    //时钟线拉高
    I2C_SCL_H;
    delay_us();//延时等待一段时间
    I2C_SDA_H;//数据线拉高

}

/*
 * 函数名: i2c_write_byte
 * 函数功能:主机向i2c总线上的从设备写8bits数据
 * 函数参数:dat : 等待发送的字节数据
 * 函数返回值: 无
 * */

void i2c_write_byte(unsigned char dat)
{  
    /*
     * 数据信号:时钟在低电平期间,发送器向数据线上写入数据
     *          时钟在高电平期间,接收器从数据线上读取数据 
     *      ----          --------
     *  SCL     \        /        \
     *           --------          --------
     *      -------- ------------------ ---
     *  SDA         X                  X
     *      -------- ------------------ ---
     *
     *      先发送高位在发送低位 
     * */
    //确保SDA是输出状态 PF15输出
    SET_SDA_OUT;
    unsigned int i;
    for(i=0;i<8;i++)
    {
        //时钟线拉低
         I2C_SCL_L;
         delay_us();//延时
         //0X3A->0011 1010   0X80->10000000
         if(dat&0X80)//最高位为1
         {
            //发送1
            I2C_SDA_H;
         }
         else  //最高位为0
         {
            I2C_SDA_L;//发送0
         }
         delay_us();//延时
         //时钟线拉高,接收器接收
         I2C_SCL_H;
        delay_us();//延时,用于等待接收器接收数据
        delay_us();//延时
        //将数据左移一位,让原来第6位变为第7位
        dat = dat<<1;

    }
    

}

/*
 * 函数名:i2c_read_byte
 * 函数功能: 主机从i2c总线上的从设备读8bits数据, 
 *          主机发送一个应答或者非应答信号
 * 函数参数: 0 : 应答信号   1 : 非应答信号
 * 函数返回值:读到的有效数据
 *
 * */
unsigned char i2c_read_byte(unsigned char ack)
{
    /*
     * 数据信号:时钟在低电平期间,发送器向数据线上写入数据
     *          时钟在高电平期间,接收器从数据线上读取数据 
     *      ----          --------
     *  SCL     \        /        \
     *           --------          --------
     *      -------- ------------------ ---
     *  SDA         X                  X
     *      -------- ------------------ ---
     *
     *      先接收高位, 在接收低位 
     * */
    unsigned int i;
    unsigned char dat;//保存接受的数据
    //将数据线设置为输入
    SET_SDA_IN;
    for(i=0;i<8;i++)
    {
        //先把时钟线拉低,等一段时间,保证发送器发送完毕数据
        I2C_SCL_L;
        delay_us();
        delay_us();//保证发送器发送完数据
        //时钟线拉高,读取数据
        I2C_SCL_H;
        delay_us();
        dat=dat<<1;//数值左移 一定要先左移在赋值,不然数据会溢出
        if(I2C_SDA_READ)//pf15管脚得到了一个高电平输入
        {
            dat |=1; //0000 0110
        }
        else
        {
            dat &=(~0X1);
        }
         delay_us();
    }
        if(ack)
        {
            i2c_nack();//发送非应答信号,不再接收下一次数据
        }
        else
        {
           i2c_ack();//发送应答信号 
        }
    return dat;//将读取到的数据返回
}
/*
 * 函数名: i2c_wait_ack
 * 函数功能: 主机作为发送器时,等待接收器返回的应答信号
 * 函数参数:无
 * 函数返回值:
 *                  0:接收到的应答信号
 *                  1:接收到的非应答信号
 * */
unsigned char i2c_wait_ack(void)
{
    /*
     * 主机发送一个字节之后,从机给主机返回一个应答信号
     *
     *                   -----------
     * SCL              /   M:读    \
     *     -------------             --------
     *     --- ---- --------------------
     * SDA    X    X
     *     ---      --------------------
     *     主  释   从机    主机
     *     机  放   向数据  读数据线
     *         总   线写    上的数据
     *         线   数据
     * */   
    //时钟线拉低,接收器可以发送信号
    I2C_SCL_L;
    I2C_SDA_H;//先把数据线拉高,当接收器回应应答信号时,数据线会拉低
    delay_us();
    SET_SDA_IN;//设置数据线为输入
    delay_us();//等待从机响应
    delay_us();
    I2C_SCL_H;//用于读取数据线数据
    if(I2C_SDA_READ)//PF15得到一个高电平输入,收到非应答信号
        return 1;
    I2C_SCL_L;//时钟线拉低,让数据线处于占用状态
    return 0;
    
} 
/*
 * 函数名: iic_ack
 * 函数功能: 主机作为接收器时,给发送器发送应答信号
 * 函数参数:无
 * 函数返回值:无
 * */
void i2c_ack(void)
{
    /*            --------
     * SCL       /        \
     *    -------          ------
     *    ---
     * SDA   X 
     *    --- -------------
     * */
    //保证数据线是输出
    SET_SDA_OUT;
    I2C_SCL_L;//拉低时钟线
    delay_us();
    I2C_SDA_L;//数据线拉低,表示应答信号
    delay_us();
    I2C_SCL_H;//时钟线拉高,等待发送器读取应答信号
    delay_us();//让从机读取我们当前的回应
    delay_us();
    I2C_SCL_L;//数据线处于占用状态,发送器发送下一次数据

}
/*
 * 函数名: iic_nack
 * 函数功能: 主机作为接收器时,给发送器发送非应答信号
 * 函数参数:无
 * 函数返回值:无
 * */
void i2c_nack(void)
{
    /*            --------
     * SCL       /        \
     *    -------          ------
     *    --- ---------------
     * SDA   X 
     *    --- 
     * */   
    //保证数据线是输出
    SET_SDA_OUT;
    I2C_SCL_L;//拉低时钟线
    delay_us();
    I2C_SDA_H;//数据线拉高,表示非应答信号
    delay_us();
    I2C_SCL_H;//时钟线拉高,等待发送器读取应答信号
    delay_us();
    delay_us();
    I2C_SCL_L;//数据线处于占用状态,发送器发送下一次数据
}

iic.h

复制代码
#ifndef __IIC_H__
#define __IIC_H__
#include "stm32mp1xx_gpio.h"
#include "stm32mp1xx_rcc.h"

/* 通过程序模拟实现I2C总线的时序和协议
 * GPIOF ---> AHB4
 * I2C1_SCL ---> PF14
 * I2C1_SDA ---> PF15
 *
 * */

#define SET_SDA_OUT     do{GPIOF->MODER &= (~(0x3 << 30)); \
                            GPIOF->MODER |= (0x1 << 30);}while(0)

#define SET_SDA_IN      do{GPIOF->MODER &= (~(0x3 << 30));}while(0)

#define I2C_SCL_H       do{GPIOF->BSRR |= (0x1 << 14);}while(0)
#define I2C_SCL_L       do{GPIOF->BRR |= (0x1 << 14);}while(0)

#define I2C_SDA_H       do{GPIOF->BSRR |= (0x1 << 15);}while(0)
#define I2C_SDA_L       do{GPIOF->BRR |= (0x1 << 15);}while(0)

#define I2C_SDA_READ    (GPIOF->IDR & (0x1 << 15))

void delay_us(void);//微秒延时
void delay(int ms);
void i2c_init(void);//初始化
void i2c_start(void);//起始信号
void i2c_stop(void);//终止信号
void i2c_write_byte(unsigned char  dat);//写一个字节数据
unsigned char i2c_read_byte(unsigned char ack);//读取一个字节数据
unsigned char i2c_wait_ack(void);       //等待应答信号
void i2c_ack(void);//发送应答信号
void i2c_nack(void);//发送非应答信号

#endif 

si7006.c

复制代码
#include "si7006.h"

//手动封装延时函数
void delay_ms(int ms)
{
    int i,j;
    for(i=0;i<ms;i++)
    {
        for(j=0;j<2000;j++)
        {} 
    }
}

void si7006_init()
{
    //1.发起起始信号
    i2c_start();
    //2.发送7bit从机地址和写标志位   0X80
    i2c_write_byte(0x80);
    //3.等待从机应答
    if(i2c_wait_ack()!=0)
    {
        return;
    }
    //4.发送寄存器地址 0XE6
    i2c_write_byte(0xE6);
    //5.等待从机应答
    if(i2c_wait_ack()!=0)
    {
        return;
    }
    //6.向从机发送数据  0X3A
    i2c_write_byte(0x3A);
    //7.等待从机应答
    if(i2c_wait_ack()!=0)
    {
        return;
    }
    //8.发送终止信号
    i2c_stop();
}


unsigned short si7006_read_hum()
{
    unsigned char hum_l,hum_h;
    unsigned short hum;
   //1.主机发起起始信号
   i2c_start();
    //2.发送7bit从机地址和写标志位   0X80
    i2c_write_byte(0x80);
    //3.等待从机应答
    if(i2c_wait_ack()!=0)
    {
        return 0;
    }
    //4.发送寄存器地址 0XE5 //湿度寄存器
    i2c_write_byte(0xE5);
    //5.等待从机应答
    if(i2c_wait_ack()!=0)
    {
        return 0;
    }
    //6.主机发起重复起始信号
    i2c_start();
    //7.主机发送7bit从机地址+1bit 读  0X81
    i2c_write_byte(0x81);
    //8.等待从机应答
    if(i2c_wait_ack()!=0)
    {
        return 0;
    }
    //9.延时等待从机测量数据
    delay_ms(10);
    //10.读取湿度的高8bit数据  hum_h
    // 11.发送应答信号
     hum_h= i2c_read_byte(0);
   // 12.读取湿度的低8位数据  hum_l
   //  13.发送非应答信号
     hum_l= i2c_read_byte(1);
    //   14.发起终止信号
     i2c_stop();
    //   15.将读取到的数据的低8位和高8bit合成一个完整的数据
    hum=hum_h<<8|hum_l;
    return hum;
}


short si7006_read_tem()
{
    char tem_l,tem_h;
    short tem;
   //1.主机发起起始信号
   i2c_start();
    //2.发送7bit从机地址和写标志位   0X80
    i2c_write_byte(0x80);
    //3.等待从机应答
    if(i2c_wait_ack()!=0)
    {
        return 0;
    }
    //4.发送寄存器地址 0XE3 //温度寄存器
    i2c_write_byte(0xE3);
    //5.等待从机应答
    if(i2c_wait_ack()!=0)
    {
        return 0;
    }
    //6.主机发起重复起始信号
    i2c_start();
    //7.主机发送7bit从机地址+1bit 读  0X81
    i2c_write_byte(0x81);
    //8.等待从机应答
    if(i2c_wait_ack()!=0)
    {
        return 0;
    }
    //9.延时等待从机测量数据
    delay_ms(10);
    //10.读取湿度的高8bit数据  hum_h
    // 11.发送应答信号
    tem_h= i2c_read_byte(0);
   // 12.读取湿度的低8位数据  hum_l
   //  13.发送非应答信号
    tem_l= i2c_read_byte(1);
    //   14.发起终止信号
     i2c_stop();
    //   15.将读取到的数据的低8位和高8bit合成一个完整的数据
    tem=tem_h<<8|tem_l;
    return tem;
}

si7006.h

复制代码
#ifndef __SI7006_H__
#define  __SI7006_H__

#include "iic.h"
void delay_ms(int ms);
void si7006_init();
unsigned short si7006_read_hum();
short si7006_read_tem();

#endif
相关推荐
努力小周6 小时前
STM32智能安防系统
c语言·stm32·单片机·嵌入式硬件·物联网·计算机网络·pcb工艺
袁小皮皮不皮7 小时前
1.HCIP BFD 学习笔记(优化版)
服务器·网络·笔记·网络协议·学习·智能路由器·ip
装不满的克莱因瓶7 小时前
【自动驾驶领域】学习 Cityscapes 数据集——城市街景语义理解的标准基准
人工智能·pytorch·python·深度学习·学习·机器学习·自动驾驶
清辞8538 小时前
产品经理需求推进流程
大数据·深度学习·学习·产品经理
华科大胡子8 小时前
在STM32上跑通TinyML
stm32·单片机·嵌入式硬件
YM52e9 小时前
鸿蒙PC ArkTS 声明合并问题深度解析与最佳实践
学习·华为·harmonyos·鸿蒙·鸿蒙系统
海兰9 小时前
【实用程序】电商销售分析仪表盘 — 从零搭建一个AI参与的全栈数据洞察系统
人工智能·学习·算法
iCxhust10 小时前
C#进程管理程序
开发语言·汇编·stm32·单片机·c#·微机原理
ken223210 小时前
在 Libreoffice Calc中输入自定义表情字符时,需要保存之后,才能正常显示
学习
zwenqiyu10 小时前
P5283 [十二省联考 2019] 异或粽子题解
c++·学习·算法