STM32 基础知识(探索者开发板)--146讲 IIC

IIC特点:

        同步串行半双工通信总线

IIC有一个弱上拉电阻,在主机和从机都没有传输数据下拉时,总线会自动上拉

SCL在低电平期间,改变SDA的值来上传数据,方便SCL电平上升时进行数据读取

SCL在高电平期间,不能改变SDA的值,若改变,SDA高到低为起始信号,低到高为终止信号

IIC配置步骤

1.使能SCL和SDA对应时钟        _HAL_RCC_GPIOB_CLK_ENABLE()

2.设置GPIO工作模式                HAL_GPIO_Init()

3.编写基本信号                起始send ack 停止send nak 应答wait ack

4.编写读和写函数                  iic_read_byte   iic_send_byte

软件驱动外设步骤

1.初始化IIC接口

2.编写写入/读取一个字节数据的函数

3.编写连续读和连续写函数

iic代码

//myiic.c

#include "./BSP/IIC/myiic.h"
#include "./SYSTEM/delay/delay.h"


//初始化IIC
void iic_init(void)
{
    GPIO_InitTypeDef gpio_init_struct;

    IIC_SCL_GPIO_CLK_ENABLE();  /* SCL引脚时钟使能 */
    IIC_SDA_GPIO_CLK_ENABLE();  /* SDA引脚时钟使能 */

    gpio_init_struct.Pin = IIC_SCL_GPIO_PIN;
    gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP;        /* 推挽输出 */
    gpio_init_struct.Pull = GPIO_PULLUP;                /* 上拉 */
    gpio_init_struct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; /* 快速 */
    HAL_GPIO_Init(IIC_SCL_GPIO_PORT, &gpio_init_struct);/* SCL */

    gpio_init_struct.Pin = IIC_SDA_GPIO_PIN;            
    gpio_init_struct.Mode = GPIO_MODE_OUTPUT_OD;        /* 开漏输出 */
    HAL_GPIO_Init(IIC_SDA_GPIO_PORT, &gpio_init_struct);/* SDA */
    /* SDA引脚模式设置,开漏输出,上拉, 这样就不用再设置IO方向了, 开漏输出的时候(=1), 也可以读取外部信号的高低电平 */

    iic_stop();     /* 停止总线上所有设备 */
}

//IIC延时函数,给芯片反应时间
static void iic_delay(void)
{
    delay_us(2);    /* 2us的延时, 读写速度在250Khz以内 */
}



//编写时序,产生IIC起始信号
void iic_start(void)
{
    IIC_SDA(1);
    IIC_SCL(1);
    iic_delay();
    IIC_SDA(0);     /* START信号: 当SCL为高时, SDA从高变成低, 表示起始信号 */
    iic_delay();
    IIC_SCL(0);     /* 下拉I2C总线,准备发送或接收数据 */
    iic_delay();
}


//编写时序,产生IIC结束信号
void iic_stop(void)
{
    IIC_SDA(0);     /* STOP信号: 当SCL为高时, SDA从低变成高, 表示停止信号 */
    iic_delay();
    IIC_SCL(1);
    iic_delay();
    IIC_SDA(1);     /* 发送I2C总线结束信号 */
    iic_delay();
}



//接收应答信号,1为接收失败,0为接收成功
uint8_t iic_wait_ack(void)
{
    uint8_t waittime = 0;
    uint8_t rack = 0;

    IIC_SDA(1);     /* 主机释放SDA线(此时外部器件可以拉低SDA线) */
    iic_delay();
    IIC_SCL(1);     /* SCL=1, 此时从机可以返回ACK应答信号 */
    iic_delay();

    while (IIC_READ_SDA)    /* 等待应答 */
    {
        waittime++;

        if (waittime > 250)
        {
            iic_stop();
            rack = 1;
            break;
        }
    }

    IIC_SCL(0);     /* SCL=0, 结束ACK检查 */
    iic_delay();
    return rack;
}

/**
 * @brief       从机产生ACK应答
 */
void iic_ack(void)
{
    IIC_SDA(0);     /* SCL = 1 时 SDA = 0,表示应答 */
    iic_delay();
    IIC_SCL(1);
    iic_delay();
    IIC_SCL(0);     /* 产生下一个时钟 */
    iic_delay();
    IIC_SDA(1);     /* 释放SDA线 */
    iic_delay();
}

/**
 * @brief       不产生ACK应答
 */
void iic_nack(void)
{
    IIC_SDA(1);     /* SCL = 1  时 SDA = 1,表示不应答 */
    iic_delay();
    IIC_SCL(1);
    iic_delay();
    IIC_SCL(0);     /* 产生下一个时钟 */
    iic_delay();
}

/**
 * @brief       IIC发送一个字节 
*/
void iic_send_byte(uint8_t data)
{
    uint8_t t;
    
    for (t = 0; t < 8; t++)
    {
        IIC_SDA(data & (0x80 >> t));    /* 高位先发送,发完后右移 */
        iic_delay();
        IIC_SCL(1);
        iic_delay();
        IIC_SCL(0);
    }
    IIC_SDA(1);         /* 发送完成, 主机释放SDA线 */
}

/**
 * @brief       IIC读取一个字节
 * @param       ack:  ack=1时,发送ack; ack=0时,发送nack
 * @retval      接收到的数据
 */
uint8_t iic_read_byte(uint8_t ack)
{
    uint8_t i, receive = 0;
    IIC_SDA(1);
    for (i = 0; i < 8; i++ )    /* 接收1个字节数据,循环八次 */
    {
        IIC_SCL(1);
        if(IIC_READ_SDA == 1){receive |= (0x80 >> i);}  //SDA为1时,读取当前这位,为0时,不读取因为本身该位为0
        IIC_SCL(0);
    }

    if (!ack)
    {
        iic_nack();     /* 发送nACK */
    }
    else
    {
        iic_ack();      /* 发送ACK */
    }

    return receive;
}


//myiic.h

#ifndef __MYIIC_H
#define __MYIIC_H

#include "./SYSTEM/sys/sys.h"


/******************************************************************************************/
/* 引脚 定义 */

#define IIC_SCL_GPIO_PORT               GPIOB
#define IIC_SCL_GPIO_PIN                GPIO_PIN_8
#define IIC_SCL_GPIO_CLK_ENABLE()       do{ __HAL_RCC_GPIOB_CLK_ENABLE(); }while(0)   /* PB口时钟使能 */

#define IIC_SDA_GPIO_PORT               GPIOB
#define IIC_SDA_GPIO_PIN                GPIO_PIN_9
#define IIC_SDA_GPIO_CLK_ENABLE()       do{ __HAL_RCC_GPIOB_CLK_ENABLE(); }while(0)   /* PB口时钟使能 */

/******************************************************************************************/

/* IO操作 */
#define IIC_SCL(x)        do{ x ? \
                              HAL_GPIO_WritePin(IIC_SCL_GPIO_PORT, IIC_SCL_GPIO_PIN, GPIO_PIN_SET) : \
                              HAL_GPIO_WritePin(IIC_SCL_GPIO_PORT, IIC_SCL_GPIO_PIN, GPIO_PIN_RESET); \
                          }while(0)       /* SCL */

#define IIC_SDA(x)        do{ x ? \
                              HAL_GPIO_WritePin(IIC_SDA_GPIO_PORT, IIC_SDA_GPIO_PIN, GPIO_PIN_SET) : \
                              HAL_GPIO_WritePin(IIC_SDA_GPIO_PORT, IIC_SDA_GPIO_PIN, GPIO_PIN_RESET); \
                          }while(0)       /* SDA */

#define IIC_READ_SDA     HAL_GPIO_ReadPin(IIC_SDA_GPIO_PORT, IIC_SDA_GPIO_PIN) /* 读取SDA */


/* IIC所有操作函数 */
void iic_init(void);            /* 初始化IIC的IO口 */
void iic_start(void);           /* 发送IIC开始信号 */
void iic_stop(void);            /* 发送IIC停止信号 */
void iic_ack(void);             /* IIC发送ACK信号 */
void iic_nack(void);            /* IIC不发送ACK信号 */
uint8_t iic_wait_ack(void);     /* IIC等待ACK信号 */
void iic_send_byte(uint8_t txd);/* IIC发送一个字节 */
uint8_t iic_read_byte(unsigned char ack);/* IIC读取一个字节 */

#endif

iic软件驱动MPU6050代码

//MPU6050.c

#include "./BSP/IIC/myiic.h"
#include "./SYSTEM/delay/delay.h"
#include "./BSP/MPU6050/MPU6050.h"

#define MPU6050_ADDRESS     0xD0

void MPU6050_WriteReg(uint8_t RegAddress,uint8_t Data)//向外设写数据
{
    iic_start();                //开始信号
    iic_send_byte(MPU6050_ADDRESS);//向该外设地址发送数据
    iic_wait_ack();                 //接收应答
    
    iic_send_byte(RegAddress);      //发送目标寄存器地址
    iic_wait_ack();
    
    iic_send_byte(Data);            //向该地址发送数据,这里是发送一个数据
    iic_wait_ack();
    
/*
    for(int i = 0;i<8; i++)          //发送多个字节数据
    {
        iic_send_byte(Data);
        iic_wait_ack();
    }
*/
    iic_stop();                   //终止信号
}

uint8_t MPU6050_ReadReg(uint8_t RegAddress)    //从外设读取数据
{
    uint*_t Data;
    
    iic_start();                //开始信号
    iic_send_byte(MPU6050_ADDRESS);//向该外设地址发送数据
    iic_wait_ack();                 //接收应答
    
    iic_send_byte(RegAddress);      //发送目标寄存器地址
    iic_wait_ack();
    
    iic_start();                //重新起始
    iic_send_byte(MPU6050_ADDRESS | 0x01)   //将最后一位变1,进行从机的读取数据
    iic_wait_ack();
    
    Data = iic_read_byte();         //读取数据
    iic_nack();                     //因为读取一个字节,所以不用产生应答
    iic_stop();
/*
    for(int i = 0;i < n;i++)        //读取n个字节,就要产生应答
    {
        Data = iic_read_byte();
        iic_ack();
    }
*/
    return Data;
}


void MPU6050_Init(void)
{
    iic_init();
    MPU6050_WriteReg(MPU6050_PWR_MGMT_1,0x01);      //解除睡眠,X轴陀螺仪时钟
    MPU6050_WriteReg(MPU6050_PWR_MGMT_2,0x01);      //6个轴都不需要待机
    MPU6050_WriteReg(MPU6050_SMPLRT_DIV,0x09);      //选择十分频的采样率
    MPU6050_WriteReg(MPU6050_CONFIG,0x06);          //滤波参数给最大
    MPU6050_WriteReg(MPU6050_GYRO_CONFIG,0x18);     //陀螺仪最大量程
    MPU6050_WriteReg(MPU6050_ACCEL_CONFIG,0x18);    //加速度计最大量程
    
}



void MPU6050_GetData(struct MPU6050_Data* p)    //获得芯片记录的各类数据
{
    uint16_t DataH,DataL;
    
    DataH = MPU6050_ReadReg(MPU6050_ACCEL_XOUT_H);  //获取加速度x轴数据
    DataL = MPU6050_ReadReg(MPU6050_ACCEL_XOUT_L);
    p->AccX = (DataH << 8) | DataL;
    
    DataH = MPU6050_ReadReg(MPU6050_ACCEL_YOUT_H);
    DataL = MPU6050_ReadReg(MPU6050_ACCEL_YOUT_L);
    p->AccY = (DataH << 8) | DataL;
    
    DataH = MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_H);
    DataL = MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_L);
    p->AccZ = (DataH << 8) | DataL;
    
    DataH = MPU6050_ReadReg(MPU6050_GYRO_XOUT_H);   //获取陀螺仪x轴数据
    DataL = MPU6050_ReadReg(MPU6050_GYRO_XOUT_L);
    p->GyroX = (DataH << 8) | DataL;
    
    DataH = MPU6050_ReadReg(MPU6050_GYRO_YOUT_H);
    DataL = MPU6050_ReadReg(MPU6050_GYRO_YOUT_L);
    p->GyroY = (DataH << 8) | DataL;
    
    DataH = MPU6050_ReadReg(MPU6050_GYRO_ZOUT_H);
    DataL = MPU6050_ReadReg(MPU6050_GYRO_ZOUT_L);
    p->GyroZ = (DataH << 8) | DataL;
}

uint8_t MPU6050_GetID(void)                 //获取芯片ID号
{
    return MPU6050_ReadReg(MPU6050_WHO_AM_I);
}

//MPU6050.h

#ifndef __MPU6050_H
#define __MPU6050_H

#include "./SYSTEM/sys/sys.h"

typedef struct MPU6050_Data{
    uint16_t AccX;      //加速度x轴数据
    uint16_t AccY;
    uint16_t AccZ;
    uint16_t GyroX;     //陀螺仪x轴数据
    uint16_t GyroY;
    uint16_t GyroZ;
}Data;

void MPU6050_WriteReg(uint8_t RegAddress,uint8_t Data);
uint8_t MPU6050_ReadReg(uint8_t RegAddress);
void MPU6050_Init(void);
uint8_t MPU6050_GetID(void);
void MPU6050_GetData(struct MPU6050_Data* p);

#define MPU6050_SMPLRT_DIV      0x19    //陀螺仪采样率,典型值:0x07(125Hz)
#define MPU6050_CONFIG          0x1A    //低通滤波频率,典型值:0x06(5Hz)
#define MPU6050_GYRO_CONFIG     0x1B    //陀螺仪自检及测量范围,典型值:0x18(不自检,2000deg/s)
#define MPU6050_ACCEL_CONFIG    0x1C    //加速计自检、测量范围及高通滤波频率,典型值:0x01(不自检,2G,5Hz)
#define MPU6050_ACCEL_XOUT_H    0x3B
#define MPU6050_ACCEL_XOUT_L    0x3C
#define MPU6050_ACCEL_YOUT_H    0x3D
#define MPU6050_ACCEL_YOUT_L    0x3E
#define MPU6050_ACCEL_ZOUT_H    0x3F
#define MPU6050_ACCEL_ZOUT_L    0x40
#define MPU6050_TEMP_OUT_H      0x41
#define MPU6050_TEMP_OUT_L      0x42
#define MPU6050_GYRO_XOUT_H     0x43
#define MPU6050_GYRO_XOUT_L     0x44
#define MPU6050_GYRO_YOUT_H     0x45
#define MPU6050_GYRO_YOUT_L     0x46
#define MPU6050_GYRO_ZOUT_H     0x47
#define MPU6050_GYRO_ZOUT_L     0x48
#define MPU6050_PWR_MGMT_1      0x6B    //电源管理,典型值:0x00(正常启用)
#define MPU6050_PWR_MGMT_2      0x6C    //电源管理寄存器2
#define MPU6050_WHO_AM_I        0x75    //IIC地址寄存器(默认数值0x68,只读)

#endif

main.c

int main(void)
{
    Data Data1;
    int ID;

    OLED_Init();        //显示屏初始化
    MPU6050_Init();      //MPU6050初始化  
    
    OLED_ShowString(1,1,"ID:");
    ID = MPU6050_GetID();        //获取芯片ID
    
    while(1)
    {
        MPU6050_GetData(&Data1);
        OLED_ShowSignedNum(2,1,Data1.AccX,5);    //获取x的加速度值
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/302112.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

微信getAccessToken限制问题

微信getAccessToken限制问题 错误代码&#xff1a;45009&#xff0c;错误信息&#xff1a;调用分钟频率受限 https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/mp-access-token/getAccessToken.html GET https://api.weixin.qq.com/cgi-bin/token 接口重复访问会…

英诺赛科PCB layout 设计案列分享1----高压单管

Layout设计中的几个关键步骤是布局、走线、铺铜、散热&#xff0c;英诺赛科高压单管GaN的Layout设计也不例外。 反激拓扑是高压单管GaN的典型应用&#xff0c;快充场合常用。该拓扑在地线的处理上都需特别注意&#xff0c;如下图所示&#xff0c;Layout时辅助绕组地、IC信号地功…

前端JS加密对抗由浅入深-2

前言&#xff1a; 本文主要讲解&#xff0c;针对前端非对称、多段加密数据传输站点&#xff0c;如何进行动态调试&#xff0c;如何进行安全测试。本次讲解不涉及任何漏洞方面&#xff0c;仅为学习探讨&#xff0c;该站点现已经更改加密方式&#xff0c;严禁非法测试&#xff0…

Uncaught (in promise) ReferenceError: require is not defined

在 Vue3 中加载项目路径下的资源图片,起初按照之前 vue 的写法 require 但浏览器却抛出了异常 Uncaught (in promise) ReferenceError: require is not defined 因为 require 采用的 webpack 加载方式,而 vue3 中通过 vite 的方式,两者存在差异,所以才产生了刚开始的一目; vu…

python基础教程八(循环1)

1. while循环 为避免多次重复的代码&#xff0c;我们会用到循环 while (condition): 执行语句 while循环的结构非常简单只要条件满足就一直循环直到&#xff0c;条件不满足为止。 例子如下&#xff1a; x1 while x<100:print(x)x1结果就是最简单的输出1-100的数字 while…

MySQL常用连接工具

Navicat 缺点&#xff1a;老版本不支持mysql8数据库连接&#xff0c;而且样式没有新版的好看 优点&#xff1a;新版支持多种数据连接&#xff0c;而且样式比较好看 HeidiSQL 官网&#xff1a;Download HeidiSQL 有一个非常好的优点就是不用输入表名称&#xff1a;双击就会自…

TS 36.211 V12.0.0-上行(4)-参考信号

本文的内容主要涉及TS 36.211&#xff0c;版本是C00&#xff0c;也就是V12.0.0。 本文具体内容为该协议的<Uplink>一章中的5.5节。

OS_lab——分页机制与内存管理

认真阅读章节资料&#xff0c;掌握什么是分页机制 调试代码&#xff0c;掌握分页机制基本方法与思路 代码pmtest6.asm中&#xff0c;212行~237行&#xff0c;设置断点调试这几个循环&#xff0c;分析究竟在这里做了什么 掌握PDE&#xff0c;PTE的计算方法 动手画一画这个映…

C# OpenCvSharp DNN 部署yoloX

目录 效果 模型信息 项目 代码 下载 C# OpenCvSharp DNN 部署yoloX 效果 模型信息 Inputs ------------------------- name&#xff1a;images tensor&#xff1a;Float[1, 3, 640, 640] --------------------------------------------------------------- Outputs ---…

Showroom Environment gallery

展示厅环境-画廊 PBR包中的所有纹理。它们适用于URP和内建。在标准状态下,所有内容都是在URP下配置的。如果你有整个场景“粉红色”,请更改渲染设置。 简单的画廊内部,配有用于照片和图片的画布。非常适合您的虚拟画廊或展厅。此套餐包含一个展厅,您可以在其中展示您的作品…

python总结-生成器与迭代器

生成器与迭代器 生成器生成器定义为什么要有生成器创建生成器的方式一(生成器表达式) 创建生成器的方式二(生成器函数)生成器函数的工作原理总结 迭代器概念可迭代对象和迭代器区别for循环的本质创建一个迭代器 动态添加属性和方法运行过程中给对象、类添加属性和方法types.Met…

我想从Spring Cloud Alibaba开始聊聊架构的本质?

另外我的新书RocketMQ消息中间件实战派上下册&#xff0c;在京东已经上架啦&#xff0c;目前都是5折&#xff0c;非常的实惠。 https://item.jd.com/14337086.html​编辑https://item.jd.com/14337086.html “RocketMQ消息中间件实战派上下册”是我既“Spring Cloud Alibaba微…

【Helm 及 Chart 快速入门】01、Helm 基本概念及仓库管理

目录 一、为何需要 Helm 二、什么是 Helm 三、Helm 核⼼概念 四、Helm 架构 五、Helm 安装 六、Helm 仓库管理 6.1 查看仓库 6.2 添加仓库 6.3 更新仓库 6.4 删除仓库 一、为何需要 Helm 在早期的 Linux 系统中&#xff0c;维护和安装软件包是⼀件极其麻烦的…

【赠书第16期】码上行动:用ChatGPT学会Python编程

文章目录 前言 1 ChatGPT简介 2 Python编程简介 3 使用ChatGPT学习Python编程 4 如何使用ChatGPT学习Python编程 5 推荐图书 6 粉丝福利 前言 随着人工智能技术的不断发展&#xff0c;聊天机器人已经成为我们日常生活和工作中不可或缺的一部分。其中&#xff0c;ChatGP…

Android系统的启动流程

Android系统启动流程大致可以概括为以下的几个步骤&#xff1a; 电源启动BootLoader启动Linux内核启动init进程启动Zygote进程启动SystemServer进程启动Launcher启动 关键的进程及其作用&#xff1a; init进程 init进程是Android系统中用户空间的第一个进程&#xff0c;进程号…

leetcode 每日一题 2023年12月28日 收集巧克力

题目 2735. 收集巧克力 给你一个长度为 n、下标从 0 开始的整数数组 nums&#xff0c;nums[i] 表示收集位于下标 i 处的巧克力成本。每个巧克力都对应一个不同的类型&#xff0c;最初&#xff0c;位于下标 i 的巧克力就对应第 i 个类型。 在一步操作中&#xff0c;你可以用成…

每个程序员都该学习的5种开发语言

我曾在某处读到过&#xff08;可能在《代码大全》&#xff0c;但我不敢确定&#xff09;&#xff0c;程序员应该每年学习一门新的编程语言。但如果做不到&#xff0c;我建议&#xff0c;你至少学习以下5种开发语言&#xff0c;以便你在职业生涯有很好的表现。 每个公司都喜爱精…

【数据结构】数据结构中应用题大全(完结)

自己在学习过程中总结了DS中几乎所有的应用题&#xff0c;可以用于速通期末考/考研/各种考试。很多方法来源于B站大佬&#xff0c;底层原理本文不做过多介绍&#xff0c;建议自己研究。例题大部分选自紫皮严书。pdf版在主页资源 一、递归时间/空间分析 1.时间复杂度的分析 设…

润和软件HopeStage与永中Office完成产品兼容性互认证

近日&#xff0c;江苏润和软件股份有限公司&#xff08;以下简称“润和软件”&#xff09;HopeStage 操作系统与永中软件股份有限公司&#xff08;以下简称“永中软件”&#xff09;永中Office办公软件完成产品兼容性测试。 测试结果表明&#xff1a;企业级通用操作系统HopeSta…

2023量子科技十大人物(团队) | 光子盒年度系列

今年&#xff0c;是量子科学与技术的又一个丰收年&#xff0c;学术研究团体和科技公司纷纷庆祝在量子计算、量子通信和量子计量学以及基础量子科学方面取得的重大成就。面对如此多令人兴奋的进展&#xff0c;我们不能不为这些进展庆祝——而所有这些的一切&#xff0c;都离不开…