STM32 HAL库F103系列之IIC实验

IIC总线协议

IIC总线协议介绍

IICInter Integrated Circuit,集成电路总线,是一种同步 串行 半双工通信总线。

总线就是传输数据通道

协议就是传输数据的规则

IIC总线结构图

① 由时钟线SCL和数据线SDA组成,并且都接上拉电阻,确保总线空闲状态为高电平

② 总线支持多设备连接,允许多主机存在,每个设备都有一个唯一的地址

③ 连接到总线上的数目受总线的最大电容400pf限制

④ 数据传输速率:标准模式100k bit/s  快速模式400k bit/s 高速模式3.4Mbit/s

 

起始信号

void iic_start(void)
{ /* SCL为高电平期间, SDA从高电平往低电平跳变*/
    	IIC_SDA ( 1 );	
	IIC_SCL ( 1 );
    	iic_delay( );
 	IIC_SDA ( 0 );	
 	iic_delay( );
    	IIC_SCL ( 0 );	
 	iic_delay( );  /* 钳住总线, 准备发送/接收数据 */
}

停止信号

void iic_stop(void)
{ /* SCL为高电平期间, SDA从低电平往高电平跳变*/
    	IIC_SDA ( 0 );	
	iic_delay( );
 	IIC_SCL ( 1 );	
 	iic_delay( );
    	IIC_SDA ( 1 ); 	/* 发送总线停止信号*/
 	iic_delay( );
}

检测应答信号

uint8_t iic_wait_ack (void) /* return 1:fail 0:succeed*/
{	
	IIC_SDA (1);  /* 主机释放SDA线 */
	iic_delay( );
	IIC_SCL (1);  /* 从机返回ACK*/ 	
 	iic_delay( );
	if ( IIC_READ_SDA ) /* SCL高电平读取SDA状态*/ 
	{
		iic_stop();	    /* SDA高电平表示从机nack */ 
		return 1;
	}
	IIC_SCL(0);	 /* SCL低电平表示结束ACK检查 */ 
 	iic_delay( );
	return 0;
}

发送应答信号

void iic_ack(void)
{ 
    	IIC_SCL (0);	
	iic_delay( );
 	IIC_SDA (0);  /* 数据线为低电平,表示应答 */
 	iic_delay( );
    	IIC_SCL (1); 	
 	iic_delay( );
}

发送非应答信号

void iic_nack(void)
{ 
    	IIC_SCL (0);	
	iic_delay( );
 	IIC_SDA (1);  /* 数据线为高电平,表示非应答 */
 	iic_delay( );
    	IIC_SCL (1); 	
 	iic_delay( );
}

发送1字节数据

void iic_send_byte(uint8_t data)
{
	for (uint8_t t = 0; t < 8; t++)
	{	/* 高位先发 */
		IIC_SDA((data & 0x80) >> 7);
 		iic_delay( );
 		IIC_SCL ( 1 );	
 		iic_delay( );
 		IIC_SCL ( 0 );
		data <<= 1; /* 左移1位, 用于下一次发送 */
	}
	IIC_SDA ( 1 ); 	/* 发送完成,主机释放SDA线 */ 
}

读取1字节数据

uint8_t iic_read_byte (uint8_t ack) /* 1:ack 0:nack*/
{ 
	uint8_t receive = 0 ;
	for (uint8_t t = 0; t < 8; t++)
	{	/* 高位先输出,先收到的数据位要左移 */ 
		receive <<= 1;		
		IIC_SCL ( 1 );	
 		iic_delay( );
		if ( IIC_READ_SDA ) receive++;
 		IIC_SCL ( 0 );
		 iic_delay( );
	}
	if ( !ack ) iic_nack();
	else iic_ack();
	return receive; 	
}

 AT24C02

原理图

EEPROM是一种掉电后数据不丢失的储存器,常用来存储一些配置信息,在系统重新上电时就可以加载。

AT24C02是一个2K bitEEPROM存储器,使用IIC通信方式。

AT24C02:写操作地址(0xA0) 读操作地址(0xA1)

AT24C02写时序

AT24C02读时序

IIC配置步骤

1、使能SCLSDA对应时钟

        __HAL_RCC_GPIOB_CLK_ENABLE()

2、设置GPIO工作模式

        SDA开漏/SCL推挽输出模式,使用HAL_GPIO_Init初始化

3、编写基本信号

        起始信号 停止信号 应答信号

4、编写读和写函数

                iic_read_byte、iic_send_byte

为什么IIC总线SDA建议用开漏模式?

开漏输出的特点:不能输出高电平,   必须有外部(或内部)上拉才能输出高电平

IICSDA脚即要作为输出,又要作为输入,用开漏输出模式,很好实现输出输入共用,避免IO模式频繁切换带来的麻烦。

输出时:主机(MCU)输出0,可以拉低信号,来实现低电平发送,主机输出1(实际不起作用),由外部上拉电阻上拉,实现高电平发送。

输入时:主机(MCU)设置输出1状态,此时因为MCU无法输出1,相当于释放了SDA脚,此时外部器件可以主动拉低SDA/释放SDA脚(同样由上拉电阻提供“输出1的功能”),实现SDA脚的高低电平变化。

由于开漏输出模式下,MCU还是可以读取IDR状态寄存器,来获取引脚高低电平,因此MCU读取IDR,即可获得SDA脚的高低电平状态,从而实现输入检测。

AT24C02配置步骤

1、初始化IIC接口

2、编写写入/读取一个字节数据函数        遵循读写时序流程编写

3、编写连续读和连续写函数        在2的基础上进行实现

实验:驱动AT24C02实现读和写1字节数据

myiic.c

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

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_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), 也可以读取外部信号的高低电平 */
}

static void iic_delay(void)
{
    delay_us(2);
}

/* 起始信号 */
void iic_start(void)
{
    /* SCL为高电平期间, SDA从高电平往低电平跳变*/
    IIC_SDA ( 1 );
    IIC_SCL ( 1 );
    iic_delay( );
    IIC_SDA ( 0 );
    iic_delay( );
    IIC_SCL ( 0 );
    iic_delay( );  /* 钳住总线, 准备发送/接收数据 */
}

/* 停止信号 */
void iic_stop(void)
{
    /* SCL为高电平期间, SDA从低电平往高电平跳变*/
    IIC_SDA ( 0 );
    iic_delay( );
    IIC_SCL ( 1 );
    iic_delay( );
    IIC_SDA ( 1 );  /* 发送总线停止信号*/
    iic_delay( );
}

/* 等待应答信号 */
uint8_t iic_wait_ack (void) /* return 1:fail 0:succeed*/
{
    IIC_SDA (1);    /* 主机释放SDA线 */
    iic_delay( );
    IIC_SCL (1);    /* 从机返回ACK*/
    iic_delay( );
    if ( IIC_READ_SDA ) /* SCL高电平读取SDA状态*/ 
    {
        iic_stop();     /* SDA高电平表示从机nack */ 
        return 1;
    }
    IIC_SCL(0);         /* SCL低电平表示结束ACK检查 */ 
    iic_delay( );
    return 0;
}

/* 应答信号 */
void iic_ack(void)
{ 
    IIC_SCL (0);
    iic_delay( );
    IIC_SDA (0);  /* 数据线为低电平,表示应答 */
    iic_delay( );
    IIC_SCL (1);
    iic_delay( );
}

/* 非应答信号 */
void iic_nack(void)
{ 
    IIC_SCL (0);
    iic_delay( );
    IIC_SDA (1);  /* 数据线为低电平,表示应答 */
    iic_delay( );
    IIC_SCL (1);
    iic_delay( );
}

/* 发送一个字节数据 */
void iic_send_byte(uint8_t data)
{
    for (uint8_t t = 0; t < 8; t++)
    {
        /* 高位先发 */
        IIC_SDA((data & 0x80) >> 7);
        iic_delay( );
        IIC_SCL ( 1 );
        iic_delay( );
        IIC_SCL ( 0 );
        data <<= 1;     /* 左移1位, 用于下一次发送 */
    }
    IIC_SDA ( 1 );      /* 发送完成,主机释放SDA线 */ 
}

/* 读取1字节数据 */
uint8_t iic_read_byte (uint8_t ack)
{ 
    uint8_t receive = 0 ;
    for (uint8_t t = 0; t < 8; t++)
    {
        /* 高位先输出,先收到的数据位要左移 */ 
        receive <<= 1;
        IIC_SCL ( 1 );
        iic_delay( );
        if ( IIC_READ_SDA ) receive++;
        IIC_SCL ( 0 );
        iic_delay( );
    }
    if ( !ack ) iic_nack();
    else iic_nack();
    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_6
#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_7
#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 */

void iic_init(void);
void iic_start(void);
void iic_stop(void);
uint8_t iic_wait_ack(void);
void iic_ack(void);
void iic_nack(void);
void iic_send_byte(uint8_t data);
uint8_t iic_read_byte (uint8_t ack);

#endif

24cxx.c

#include "./BSP/IIC/myiic.h"
#include "./BSP/24CXX/24cxx.h"
#include "./SYSTEM/delay/delay.h"

void at24c02_init(void)
{
    iic_init();
}

void at24c02_write_one_byte(uint8_t addr, uint8_t data)
{
    /* 1、发送起始信号 */
    iic_start();
    
    /* 2、发送通讯地址(写操作地址) */
    iic_send_byte(0xA0);
    
    /* 3、等待应答信号 */
    iic_wait_ack();
    
    /* 4、发送内存地址 */
    iic_send_byte(addr);
    
    /* 5、等待应答信号 */
    iic_wait_ack();
    
    /* 6、发送写入数据 */
    iic_send_byte(data);
    
    /* 7、等待应答信号 */
    iic_wait_ack();
    
    /* 8、发送停止信号 */
    iic_stop();
    
    /* 等待EEPROM写入完成 */
    delay_ms(10);
}

uint8_t at24c02_read_one_byte(uint8_t addr)
{
    uint8_t rec = 0;
    
    /* 1、发送起始信号 */
    iic_start();
    
    /* 2、发送通讯地址(写操作地址) */
    iic_send_byte(0xA0);
    
    /* 3、等待应答信号 */
    iic_wait_ack();
    
    /* 4、发送内存地址 */
    iic_send_byte(addr);
    
    /* 5、等待应答信号 */
    iic_wait_ack();
    
    /* 6、发送起始信号 */
    iic_start();
    
    /* 7、发送通讯地址(读操作地址) */
    iic_send_byte(0xA1);
    
    /* 8、等待应答信号 */
    iic_wait_ack();
    
    /* 9、等待接收数据 */
    rec = iic_read_byte(0);
    /* 10、发送非应答(获取该地址即可) */
    
    /* 11、发送停止信号 */
    iic_stop();
    
    return rec;
}

24cxx.h

#ifndef __24CXX_H
#define __24CXX_H

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

void at24c02_init(void);
void at24c02_write_one_byte(uint8_t addr, uint8_t data);
uint8_t at24c02_read_one_byte(uint8_t addr);

#endif


main.c

#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"
#include "./USMART/usmart.h"
#include "./BSP/LED/led.h"
#include "./BSP/LCD/lcd.h"
#include "./BSP/KEY/key.h"
#include "./BSP/24CXX/24cxx.h"

int main(void)
{
    uint8_t key;
    uint8_t i = 0;
    uint8_t data = 0;
    
    HAL_Init();                                 /* 初始化HAL库 */
    sys_stm32_clock_init(RCC_PLL_MUL9);         /* 设置时钟, 72Mhz */
    delay_init(72);                             /* 延时初始化 */
    usart_init(115200);                         /* 串口初始化为115200 */
    led_init();                                 /* 初始化LED */
    key_init();                                 /* 初始化按键 */
    at24c02_init();
    
    while (1)
    {
        key = key_scan(0);

        if (key == KEY1_PRES)
        {
            at24c02_write_one_byte(100, 66);
            printf("write data \r\n");
        }

        if (key == KEY0_PRES)
        {
            data = at24c02_read_one_byte(100);
            printf("read data:%d \r\n", data);
        }

        i++;

        if (i % 20 == 0)
        {
            LED0_TOGGLE();  /* 红灯闪烁 */
            i = 0;
        }

        delay_ms(10);
    }
}

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

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

相关文章

机器学习:深入解析SVM的核心概念(问题与解答篇)【一、间隔与支持向量】

直接阅读原始论文可能有点难和复杂&#xff0c;所以导师直接推荐我阅读周志华的《西瓜书》&#xff01;&#xff01;然后仔细阅读其中的第六章&#xff1a;支持向量机 间隔与支持向量 问题一&#xff1a;什么叫法向量&#xff1f;为什么是叫法向量 在这个线性方程中&#xff…

Apache Seata如何解决TCC 模式的幂等、悬挂和空回滚问题

title: 阿里 Seata 新版本终于解决了 TCC 模式的幂等、悬挂和空回滚问题 author: 朱晋君 keywords: [Seata、TCC、幂等、悬挂、空回滚] description: Seata 在 1.5.1 版本解决了 TCC 模式的幂等、悬挂和空回滚问题&#xff0c;这篇文章主要讲解 Seata 是怎么解决的。 今天来聊一…

SQLite尽如此轻量

众所周知&#xff0c;SQLite是个轻量级数据库&#xff0c;适用于中小型服务应用等&#xff0c;在我真正使用的时候才发现&#xff0c;它虽然轻量&#xff0c;但不知道它却如此轻量。 下载 官网&#xff1a; SQLite Download Page 安装 1、将下载好的两个压缩包同时解压到一个…

【Vue3+Tres 三维开发】02-Debug

预览 介绍 Debug 这里主要是讲在三维中的调试,同以前threejs中使用的lil-gui类似,TRESJS也提供了一套可视化参数调试的插件。使用方式和之前的组件相似。 使用 通过导入useTweakPane 即可 import { useTweakPane, OrbitControls } from "@tresjs/cientos"const {…

大数据面试题 —— Spark数据倾斜及其解决方案

目录 1 调优概述2 数据倾斜发生时的现象3 数据倾斜发生的原理4 如何定位导致数据倾斜的代码4.1 某个 task 执行特别慢的情况4.2 某个 task 莫名其妙内存溢出的情况5 查看导致数据倾斜的 key 的数据分布情况6 数据倾斜的解决方案6.1 使用 Hive ETL 预处理数据6.2 过滤少数导致倾…

Xcode 15构建问题

构建时出现的异常&#xff1a; 解决方式&#xff1a; 将ENABLE_USER_SCRIPT_SANDBOXING设为“no”即可&#xff01;

【Linux命令行艺术】1. 初见命令行

&#x1f4da;博客主页&#xff1a;爱敲代码的小杨. ✨专栏&#xff1a;《Java SE语法》 | 《数据结构与算法》 | 《C生万物》 |《MySQL探索之旅》 |《Web世界探险家》 ❤️感谢大家点赞&#x1f44d;&#x1f3fb;收藏⭐评论✍&#x1f3fb;&#xff0c;您的三连就是我持续更…

基于自注意力机制的长短期记忆神经网络(LSTM-SelfAttention)的回归预测

提示&#xff1a;MATLAB版本需要R2023a以上 基于自注意力机制的长短期记忆神经网络&#xff08;LSTM-SelfAttention&#xff09;是一种用于时序数据预测的模型。这个模型结合了两个不同的结构&#xff0c;即长短期记忆网络&#xff08;LSTM&#xff09;和自注意力机制&#xff…

ddos云服务器有哪些防御方法和优势

本文将介绍云服务器遇到DDoS攻击的应对方法&#xff0c;包括流量清洗、负载均衡、防火墙设置和CDN加速等。同时&#xff0c;文章还介绍了ddos云服务器的防御优势&#xff0c;包括高防护能力、自动化防御、实时监控和报警以及弹性扩展等。通过这些防御方法和ddos云服务器的应用&…

pve(Proxmox VE)安装i225v网卡驱动

配置pve源 备份原来的源 mv /etc/apt/sources.list /etc/apt/sources.list.bak打开文件 vi /etc/apt/sources.list将以下内容粘贴进去 deb https://mirrors.tuna.tsinghua.edu.cn/debian/ bookworm main contrib non-free non-free-firmwaredeb https://mirrors.tuna.tsing…

连接oracle时出现ORA-12541:TNS:无监听程序的错误

遇到个问题&#xff0c;有一台windows serve 的服务器&#xff0c;这台服务器&#xff08;只部署了oracle&#xff09;忽然监听出问题了&#xff0c;提示 一、问题检查步骤&#xff1a; 1.winR--->cmd--->输入 lsnrctl status 查看监听的状态 如果监听器未运行&#…

Swift - 函数

文章目录 Swift - 函数1. 函数的定义2. 隐式返回(Implicit Return)3. 返回元组&#xff1a;实现多返回值4. 函数的文档注释5. 参数标签&#xff08;Argument Label&#xff09;6. 默认参数值&#xff08;Default Parameter Value&#xff09;7. 可变参数&#xff08;Variadic P…

MAC有没有免费NTFS tuxera激活码 tuxera破解 tuxera for mac2023序列号直装版 ntfs formac教程

Tuxera NTFS 2023破解版是一款非常好用的在线磁盘读写工具&#xff0c;该软件允许mac用户在Windows NTFS格式的硬盘上进行读写操作&#xff0c;Mac的文件系统是HFS&#xff0c;而Windows则使用NTFS格式&#xff0c;这导致在Mac系统上不能直接读写Windows格式的硬盘。然而&#…

PeLK: 大卷积核强势回归,高达101 × 101,提出了外围卷积

paper&#xff1a;https://arxiv.org/pdf/2403.07589 code&#xff1a;暂无 目录 0. 摘要 1. 引言 2. 相关工作 2.1. Large Kernel Convolutional Networks 2.2. Peripheral Vision for Machine Learning 3. 密集卷积优于条纹卷积 4. 参数高效的大核卷积神经网络 4.1. …

RabbitMQ知识点总结(一)

为什么要使用RabbitMQ? 异步&#xff0c;解耦&#xff0c;削峰。 异步 提高效率&#xff1b;一个挂了&#xff0c;另外的服务不受影响。 解耦 增加或减少服务比较方便。 削峰 每天0点到16点&#xff0c;A系统风平浪静&#xff0c;每秒并发数量就100个。结果每次到了16点到…

STM32修改主频的方法

大家都知道STM32F103C8T6的主频是72M&#xff0c;那怎么样才能在程序中获得这个主频的值呢&#xff1f;怎么样才能更改主频的值呢&#xff1f; 如图找到主频的变量&#xff0c;然后显示这个变量就是显示主频了。 #include "stm32f10x.h" // Device…

含匹配扰动的多智能体领航跟随一致性Matlab仿真

文章目录 [TOC](文章目录) 前言一、问题描述二、基于LQR的观测器和控制器设计1.观测器设计2.控制器设计 三、数值仿真四、参考文献总结 前言 ​本文探讨了带有匹配扰动的多智能体领航跟随一致性控制方法&#xff0c;并提供了相应的Matlab仿真代码。 具体的设计步骤如下&#…

Mysql的索引与事务理解

目录 一、Mysql索引 1、索引的概念 2、索引的特点 3、索引使用场景 4、Mysql有关索引的操作 &#xff08;1&#xff09;查询表具有的索引 &#xff08;2&#xff09;增加索引 &#xff08;3&#xff09;删除索引 5、索引实现原理 &#xff08;1&#xff09;B树 &…

【java数据结构之八大排序(上)-直接插入排序,希尔排序,选择排序,堆排序,向下调整(大根堆,小根堆)等知识详解】

&#x1f308;个人主页&#xff1a;努力学编程’ ⛅个人推荐&#xff1a;基于java提供的ArrayList实现的扑克牌游戏 |C贪吃蛇详解 ⚡学好数据结构&#xff0c;刷题刻不容缓&#xff1a;点击一起刷题 &#x1f319;心灵鸡汤&#xff1a;总有人要赢&#xff0c;为什么不能是我呢 …

深度学习之基于YOLOv5的山羊行为识别系统

欢迎大家点赞、收藏、关注、评论啦 &#xff0c;由于篇幅有限&#xff0c;只展示了部分核心代码。 文章目录 一项目简介 二、功能三、系统四. 总结 一项目简介 基于YOLOv5的山羊行为识别系统是一个结合了深度学习和目标检测技术的项目&#xff0c;旨在通过图像或视频实现对山羊…