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

news2024/9/22 13:38:59

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的加速度值
    }
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

第二证券:如何解决股票亏损问题?

1、进行补仓 假设该股票基本面较好只是暂时的出现下跌&#xff0c;那么就可以选择补仓。出资者在亏本过程中&#xff0c;可以经过补仓来增加持仓数量&#xff0c;从而下降其持仓本钱&#xff0c;等候股票的反弹&#xff0c;来抵达快速解套的意图。 2、高抛低吸、做T获利弥补 …

书生·浦语大模型趣味 Demo笔记及作业

文章目录 笔记作业基础作业&#xff1a;进阶作业&#xff1a; 笔记 书生浦语大模型InternLM-Chat-7B 智能对话 Demo&#xff1a;https://blog.csdn.net/m0_49289284/article/details/135412067书生浦语大模型Lagent 智能体工具调用 Demo&#xff1a;https://blog.csdn.net/m0_…

018、通用集合类型

Rust标准库包含了一系列非常有用的被称为集合的数据结构。大部分的数据结构都代表着某个特定的值&#xff0c;但集合却可以包含多个值。 与内置的数组与元组类型不同&#xff0c;这些集合将自己持有的数据存储在了堆上。这意味着数据的大小不需要在编译时确定&#xff0c;并且可…

【Docker】数据卷容器

多个容器进行数据交换 这里引入一个数据卷容器的概念 以下介绍容器A与容器B进行数据交换的原理 假如容器A要与容器 B 进行数据交换&#xff0c; 首先创建一个容器C&#xff0c;将他挂载到数据卷&#xff0c;然后再将容器A与容器B挂载到容器C&#xff0c;这样做相当于容器A与…

在版权付费方面,OpenAI 比人想象中的还要「小气」

随着新闻出版商与AI公司达成“使用新闻训练AI模型”的协议&#xff0c;像 OpenAI 等科技企业愿意为受版权保护的信息支付的价格逐渐浮出水面。 据 The Information 报道&#xff0c;OpenAI 每年愿意向出版商提供 100万到500万美元来支付受版权保护的新闻文章训练其AI模型。 但…

LaTex的下载与安装(Texlive+TexStudio,2023版)

目录 1. Texlive的下载与安装2. TexStudio的下载与安装 LaTex的下载与安装涉及到环境配置和编辑器安装&#xff0c;本文主要根据一下两个较为常用的组合进行下载和安装&#xff1a; Texlive&#xff08;是必须安装的LaTex环境&#xff09;&#xff1b;TexStudio&#xff08;是…

数据挖掘总结(考试版)

数据挖掘总结&#xff1a; 第一章&#xff1a; 数据挖掘KDD步骤&#xff1a; 数据清理: (消除噪声和删除不一致的数据)数据集成&#xff08;多种数据源可以组合在一起&#xff09;数据选择&#xff08;从数据库中提取与分析任务相关的数据&#xff09;数据变换&#xff08;数…

基于海思SD3403/3519AV200的医疗内窥镜技术框架

医疗内窥镜市场&#xff0c;经过多年的发展&#xff0c;产品种类繁多&#xff0c;应用场景更加的多样了&#xff0c;但是基础的技术方案非常的收敛&#xff0c;主流的方案就是海思的SOC和FPGA。海思的SOC以优秀的图像质量&#xff0c;和便携的开发占据了大量的硬镜应用&#xf…

2024年1月7日15:09:50

2024年1月7日15:09:55复习&#xff1a;我今天学了有价值的东西&#xff0c;那就是在瓦罗兰特拿到了三杀 2024年1月7日15:11:10学习了如何使用vivopad2的键盘 可以稍微用一下 2024年1月7日15:17:58 学习一个编程的题目 2024年1月7日15:31:27不用机械键盘打字效率就是比不用低…

系列六、MindManager取消首字母自动大写

一、MindManager取消首字母自动大写 1.1、步骤 主页>字体>设置字体样式>格式字体>文本和大写>文本大写>无 1.2、参考 https://tieba.baidu.com/p/3752136361

私有仓库Gogs搭建(docker环境)

文章目录 环境准备Gogs简介MYSQL(docker) 搭建gogs(docker) 部署gogs初始化配置配置管理员信息仓库创建项目代码上传仓库 环境准备 本地环境安装git,参考Git分布式版本控制工具学习管理面板1panel&#xff0c;安装参考Armbian安装1panel教程服务器docker环境&#xff08;如果使…

【第6期】使用Iview的Select组件进行远程搜索并在编辑时设置一个或多个默认值

本期简介 下拉框这个组件用的地方非常多&#xff0c;普通用法就是将数据列表一次性查询渲染&#xff0c;在列表里面直接本地搜索&#xff0c;优点是可缓存、速度快&#xff0c;但在某些场合并不适用&#xff0c;比如要在下拉框中选择一所中国的学校&#xff0c;幼儿园/小学/初…

PAT 乙级 1076 Wifi密码

解题思路&#xff1a;首先这题看着复杂实际很简单&#xff0c;每题就一个正确的我们把他找出来就可以&#xff0c;之后利用和’A’ 相减加一 求出数值&#xff0c;输出即可。python代码更容易 c语言: #include<stdio.h> int main() {int n,i,j,count0;scanf("%d&q…

ssm基于Vue.js的网上招聘系统设计与实现+vue论文

摘 要 计算机网络发展到现在已经好几十年了&#xff0c;在理论上面已经有了很丰富的基础&#xff0c;并且在现实生活中也到处都在使用&#xff0c;可以说&#xff0c;经过几十年的发展&#xff0c;互联网技术已经把地域信息的隔阂给消除了&#xff0c;让整个世界都可以即时通话…

MySQL视图 索引 面试题

一. 视图 视图&#xff1a;一种虚拟存在的表&#xff0c;行和列的数据来自定义视图的查询中使用的表&#xff0c;并且是在使用视图时动态生成的&#xff0c;只保存了sql逻辑&#xff0c;不保存查询结果 视图语法 -- 创建 create view 视图名 as 查询语句;-- 使用 select * f…

为什么要使用云原生数据库?云原生数据库具体有哪些功能?

相比于托管型关系型数据库&#xff0c;云原生数据库极大地提高了MySQL数据库的上限能力&#xff0c;是云数据库划代的产品&#xff1b;云原生数据库最早的产品是AWS的 Aurora。AWS Aurora提出来的 The log is the database的理念&#xff0c;实现存储计算分离&#xff0c;把大量…

imgaug库指南(九):从入门到精通的【图像增强】之旅

引言 在深度学习和计算机视觉的世界里&#xff0c;数据是模型训练的基石&#xff0c;其质量与数量直接影响着模型的性能。然而&#xff0c;获取大量高质量的标注数据往往需要耗费大量的时间和资源。正因如此&#xff0c;数据增强技术应运而生&#xff0c;成为了解决这一问题的…

qt-C++笔记之QProcess

qt-C笔记之QProcess code review! 文章目录 qt-C笔记之QProcess一.示例&#xff1a;QProcess来执行系统命令ls -l命令并打印出结果说明 二.示例&#xff1a;QProcess来执行系统命令ls -l命令并打印出结果&#xff0c;代码进一步丰富三.示例&#xff1a;使用 QProcess 在 Qt 中…

ROS+moveit+jakaminicob仿真运动

先浅浅的放一个官方的c文档&#xff1a; Motion Planning API — moveit_tutorials Melodic documentation 目录 一、实现运动到目标点的程序 二、在rviz里面新建扫描平台 一、实现运动到目标点的程序 &#xff08;等我得空了补一个c运行环境部署说明&#xff09; #inclu…