I2C

I2C简介

I2C是一种两线的协议,一条使SCL,一条是SDA。
他们两个分别承担时钟线和数据线的作用,

  1. 时钟线SCL用来保持与主机的同步通信;
  2. 数据线SDA用于数据的发送与接收。
    SCL的英文是 Slave Clock ,意思是给从机的时钟
    SDA的英文是 Slave Data ,意思是给从机的数据

I2C的4种信号与5种速率

4种信号

I2C协议最基础的几种信号:起始、停止、应答和非应答信号

1、起始信号与停止信号

在一个时间区间内:

  • 先把SDA给拉低电平,再把SCL拉底电平就是起始信号(两个信号原先默认是高电平);
  • 先把SCL给拉高电平,再把SCL拉高电平就是终止信号;(两个信号原先默认是低电平)

2、发送应答与接收应答

如图所示:

3、发送一个字节

4、接收一个字节

5种速率

I2C协议可以工作在以下5种速率模式下,不同的器件可能支持不同的速率。

  • 标准模式(Standard):100kbps
  • 快速模式(Fast):400kbps
  • 快速模式+(Fast-Plus):1Mbps
  • 高速模式(High-speed):3.4Mbps
  • 超快模式(Ultra-Fast):5Mbps(单向传输)

在cubemx中配置f1芯片的时候只有前面两种,标准模式和快速模式。

I2C与多设备通信

一组I2C总线上可以挂载多个设备,如图所示:

当主机要与某一个I2C设备进行通信的时候,因为当前的线上挂载了多个设备,所以此时的信号相当于是以广播的形式发送出去的,所有设备都会接收到这个信号。
但是当前我只想和其中一个设备通信该怎么办呢?为了针对这种情况,于是每个设备都有了一个对应的设备地址(这个设备地址是由厂家设定的,同一种设备的设备地址是一样的。如,有两个AT24C02的芯片,他们的设备地址就是一样的)

我们要与设备进行通信会先发一个起始信号,再发送一个设备地址字节信号,所有的设备都会接收到这串消息,但是只有与自己的设备号对应上了,才能算是开始通信。

对于AT24C02的设备地址信号就是0xA0和0xA1(在A0、A1、A2引脚都拉地的情况下),这两个字节的设备地址的区别就是在与写和读的区别,0xA0是写设备信号,而0xA1是读设备信号。

AT24C02

AT24C02简介

AT24C02是ATMAL公司开发的串行 COMS 型 EEPROM存储类芯片,与他同类型的芯片还有AT24C01A、AT24C04、AT24C08A、AT24C016A,
这些芯片唯一的区别就是存储容量不同,后面是1的就是1Kbit、后面是2的就是2Kbit,以此类推。

芯片引脚介绍

AT24C02有8个引脚:

  • A0、A1、A2:这三个是设备地址选择引脚
  • WP:写保护引脚,高低平有效,一般给他拉底电平
  • SCL:I2C时钟线
  • SDA:I2C数据线

AT24C02地址选择

对于AT24C02来说,如果把A0、A1、A2都拉到地上,那么器件地址就是0xA0或0xA1,一般来说也是把A0、A1、A2都拉到地上

CubeMX的配置

这里用的芯片是野火的指南者STM32F103VET6

新建工程

1. 打开 STM32CubeMX 软件,点击“新建工程”

2. 选择 MCU 和封装

3.配置时钟

RCC 设置,选择 HSE(外部高速时钟) 为 Crystal/Ceramic Resonator(晶振/陶瓷谐振器)

选择 Clock Configuration,配置系统时钟 SYSCLK 为 72MHz
修改 HCLK 的值为 72 后,输入回车,软件会自动修改所有配置

4. 配置调试模式

非常重要的一步,否则会造成第一次烧录程序后续无法识别调试器
SYS 设置,选择 Debug 为 Serial Wire

5. I2C参数配置

选择I2C1中打开I2C,右边会出现PB7为SDA、PB6为SCL。

这些配置从上到下分别是:

  • I2C Speed Mode:配置I2C速率,这里有标准模式、快速模式。
  • I2C Clock Speed(Hz):这里不用配置,上面,那个Speed Mode配置好了,这里会默认变成对应的速率。
  • Clock No Stretch Mode:时钟不拉伸,这里选择disabled,意思是选择不拉伸时钟,感觉这里就是为了匹配一些速度慢设备的,而专门搞了一个配置。
  • Primary Address Length selection:设备地址长度选择,有7位的有10位的,默认7位即可。
  • 后面的其他配置我不懂,但我知道配置默认即可。

6_1. 工程配置1

6_2. 工程配置2

这里要注意,工程名一定不能有中文,并且路径下也一定不能有中文

7生成工程

代码部分

AT24C02.h

#ifndef __AT24C02_H_
#define __AT24C02_H_#include "i2c.h"//修改对应的I2C句柄
#define AT24C02_I2C hi2c1void EEPOM_Clear_Single(uint16_t i);                       //使第i个字节的值归0
void EEPOM_Empty(void);                                                 //使清空EEPOM内存
void Wrinte_EEPOM(uint16_t i, uint8_t data);        //在第i个字节的位置写入SIZE__大小的data
uint8_t Read_EEPOM(uint16_t i);                                 //读取第个字节的数据#endif

AT24C02.c

#include "stdarg.h"
#include "AT24C02.h"#define ADDR_24LCxx_Write 0xA0
#define ADDR_24LCxx_Read 0xA1#define AT24C02_NUM_Byte 256   //AT24C02的大小/*清除单个位置的数据*/
void EEPOM_Clear_Single(uint16_t i)
{uint8_t clear__ = 0;HAL_I2C_Mem_Write(&AT24C02_I2C, ADDR_24LCxx_Write, i, I2C_MEMADD_SIZE_8BIT, &clear__, 1, 1000);
}/*清除全部数据*/
void EEPOM_Empty(void)
{uint8_t clear__ = 0;for(uint16_t i=0; i<256; i++){EEPOM_Clear_Single(i);}
}void Wrinte_EEPOM(uint16_t i, uint8_t data)
{/* wrinte date to EEPROM */HAL_I2C_Mem_Write(&AT24C02_I2C, ADDR_24LCxx_Write, i, I2C_MEMADD_SIZE_8BIT, &data, 1, 1000);
}uint8_t Read_EEPOM(uint16_t i)
{uint8_t data;/* read date from EEPROM */HAL_I2C_Mem_Read(&AT24C02_I2C, ADDR_24LCxx_Read, i, I2C_MEMADD_SIZE_8BIT, &data, 1, 1000);return data;
}

Login.h

#ifndef LOGIN__H___
#define LOGIN__H___#include "usart.h"/*修改对应串口句柄*/
#define     PASS_UART_HAND          huart1
#define     PASS_UART                   USART1/*****密码长度选择*****/
typedef enum{PASS_LENGTH_4  = 4,PASS_LENGTH_5  = 5,PASS_LENGTH_6  = 6,PASS_LENGTH_7  = 7,PASS_LENGTH_8  = 8,
}PASSWORD_LENGTH;/*****AT中分给密码区的空间*****/
typedef enum{AT_DOWN_PA_RE = 0,//密码下限区AT_UP___PA_RE = 7,//密码上限区AT_PASS__SIZE = 8
}PASSWORD_REGION;/*****密码类型*****/
typedef enum{LOGGED_NOT     = 0,//未登入LOGGING_IN        = 1,//登入中
}LOGIN_STATUS;/*****密码选择确认或者撤销*****/
typedef enum{PASS_REVOKE  = 11,        //撤销PASS_CONFIRM = 12,     //确认PASS_EMPTY   = 13,     //清空
}PASS_YES_OR_NO;/*****登入后其他操作*****/
typedef enum{CHANGE_PASS    = 14,      //修改密码RESET_PASS    = 15,      //重置密码EXIT_LOGIN    = 16       //退出登入
}LOGIN_IN_OPERATION;/*密码设定长度*/
#define     PASSLEN     PASS_LENGTH_4/*****密码句柄*****/
typedef struct Pass_Hand{uint8_t Login_Status;                                              //登入状态,取值在LOGIN_STATUS中uint8_t * Password_Buf;                                           //密码缓冲区uint8_t a_Password_Buf;                                          //密码中转站uint8_t  Password_Buf_Cnt;                                       //密码缓存计数ErrorStatus (*Init_PossWord)(void);                 //初始化函数ErrorStatus (*Reset_PossWord)(void);             //复位清除原来的密码,并重新初始化密码ErrorStatus (*Revise_PossWord)(uint8_t*);        //修改密码,用新的密码替换原来的密码ErrorStatus (*Match_PossWord)(uint8_t*);      //密码匹配,拿用户输入的密码和设定的密码进行匹配//  struct Pass_Hand* yxypass;
}Password_hand;extern Password_hand yxypassword;        //句柄/*函数区*//*输入密码提示*/
void Password_Promot(void);/****未登入回调函数****/
/*未登入:
--  撤销密码
--  确认密码
--  清空密码
--  其他:--    判断密码是否超值:--  超值,舍弃--  没超值,输入一位密码
*/
void Not_Login_in(void);/****已登入回调函数****/
/*已登入:
--  退出登入
--  确认密码
--  清空密码
--  其他:--    判断密码是否超值:--  超值,舍弃--  没超值,输入一位密码
*/
void Login_in(void);#endif  /*LOGIN__H___*/

Login.c

#include "AT24C02.h"
#include "Login.h"#include "redirect.h"                                                     //试着删#define D_MS_Len   2/*内部函数声明区*/
static ErrorStatus Password_Init(void);
static ErrorStatus Password_Reset(void);
static ErrorStatus Password_Revise(uint8_t* User_Pass);
static ErrorStatus Password_Match(uint8_t* User_Input_Pass);static uint8_t PasswordBuf[PASS_LENGTH_8] = {0};   //密码缓冲区,按最大密码量来设置大小/*密码延时*/
#define Pass_Delay_Ms yxy_delay_msPassword_hand yxypassword = {LOGGED_NOT,         //登入状态(未登入)PasswordBuf,       //密码缓存区0,                           //密码中转站0,                           //密码计数Password_Init,        //初始化密码函数Password_Reset,        //重置密码函数Password_Revise,    //修改密码Password_Match        //密码匹配
};/*AT24C02中的密码区(个人设定):0~7字节的位置
*/
void yxy_delay_us(uint32_t us)
{SysTick->CTRL =0;  SysTick->LOAD =72*us-1;SysTick->VAL =0; SysTick->CTRL =5;   while((SysTick->CTRL & 0x10000) == 0);SysTick->CTRL =0;
}void yxy_delay_ms(uint32_t ms)
{while(ms--){SysTick->CTRL =0;  SysTick->LOAD =72000-1;SysTick->VAL =0; SysTick->CTRL =5;   while((SysTick->CTRL & 0x10000) == 0);}SysTick->CTRL =0;
}/****初始密码****/
/*初始一个默认密码:1234……
*/
/*(SUCCESS)0 是重置成功的(ERROR  )1 是重置失败的
*/
static ErrorStatus Password_Init(void)
{/*默认密码*/uint8_t DefaultPassword[PASSLEN] = {1,1,1,1};/*在密码区中对密码初始化*/for(uint8_t i=AT_DOWN_PA_RE; i<AT_UP___PA_RE; i++){if(i>=PASSLEN)   //判断当前数值是否超过密码设定值{break;}Wrinte_EEPOM(i, DefaultPassword[i]);       //写入默认密码Pass_Delay_Ms(D_MS_Len);if(Read_EEPOM(i) != DefaultPassword[i])    //判断是否清除成功{return ERROR;}Pass_Delay_Ms(D_MS_Len);}return SUCCESS;
}/****密码重置****/
/*(SUCCESS)0 是重置成功的(ERROR  )1 是重置失败的
*/
static ErrorStatus Password_Reset(void)
{/*在密码区中对密码重置*/for(uint8_t i=AT_DOWN_PA_RE; i<AT_UP___PA_RE; i++){if(i>=PASSLEN)  //判断密码是否超过密码设定值{break;}Wrinte_EEPOM(i, 0);          //把使用密码区的密码清零Pass_Delay_Ms(D_MS_Len);if(Read_EEPOM(i) != 0)    //判断是否清除成功{return ERROR;}Pass_Delay_Ms(D_MS_Len);}Password_Init();  //复位后初始化密码return SUCCESS;
}/****修改密码****/
/*(SUCCESS)0 是重置成功的(ERROR  )1 是重置失败的
*/
static ErrorStatus Password_Revise(uint8_t* User_Pass)
{/*在密码区中修改密码*/for(uint8_t i=AT_DOWN_PA_RE; i<AT_UP___PA_RE; i++){if(i>=PASSLEN)   //判断密码是否超过密码设定值{break;}Wrinte_EEPOM(i, User_Pass[i]);//在EEPOM中写入密码Pass_Delay_Ms(D_MS_Len);if(Read_EEPOM(i) != User_Pass[i])    //不等于就直接退出{return ERROR;}Pass_Delay_Ms(D_MS_Len);}return SUCCESS;
}/****匹配密码****/
/*(SUCCESS)0 是重置成功的(ERROR  )1 是重置失败的
*/
static ErrorStatus Password_Match(uint8_t* User_Input_Pass)
{/*在密码区中匹配密码*/for(uint8_t i=AT_DOWN_PA_RE; i<AT_UP___PA_RE; i++){if(i>=PASSLEN)   //判断密码是否超过密码设定值{break;}if(User_Input_Pass[i] != Read_EEPOM(i))//密码一个一个比较{return ERROR;}}return SUCCESS;
}void Password_Input_sigle(void)
{}/****密码提示****/
void Password_Promot(void)                                                      //试着删
{printf("请输入密码:\r\n");
}/****未登入回调函数****/
/*未登入:
--  撤销密码
--  确认密码
--  其他:--    判断密码是否超值:--  超值,舍弃--  没超值,输入一位密码
*/
void Not_Login_in(void)
{switch(yxypassword.a_Password_Buf){case PASS_REVOKE:       //撤销输入一位if(yxypassword.Password_Buf_Cnt > 0) //减少计数后直接退出{printf("清除1位\r\n");                                                       //试着删yxypassword.Password_Buf_Cnt--;}break;case PASS_CONFIRM:   //确认密码printf("12的进入\r\n");                                                        //试着删if(yxypassword.Password_Buf_Cnt == PASSLEN)  //判断是否密码输入完{if(yxypassword.Match_PossWord(yxypassword.Password_Buf) == SUCCESS)   //判断是否匹配正确{yxypassword.Login_Status = LOGGING_IN;      //登入成功,进入已登入状态yxypassword.Password_Buf_Cnt=0;                       //登入成功,清零计数位printf("登入成功\r\n");                                                        //试着删break;}}printf("登入失败\r\n");                                                      //试着删break;case PASS_EMPTY: //清空输入密码printf("清空\r\n");                                                     //试着删yxypassword.Password_Buf_Cnt = 0; //清空输入计数break;default:if(yxypassword.Password_Buf_Cnt >= PASSLEN)       //判断是否密码输入超值,超值丢弃,不超存入{printf("超值舍弃\r\n");                                                      //试着删}else{//把中转站的值赋给缓冲区yxypassword.Password_Buf[yxypassword.Password_Buf_Cnt] = yxypassword.a_Password_Buf;yxypassword.Password_Buf_Cnt++;  //密码数量加1}break;}
}/****已登入回调函数****/
/*已登入:
--  退出登入
--  确认密码
--  其他:--    判断密码是否超值:--  超值  ,舍弃--    没超值,输入一位密码
*/void Login_in(void)
{static uint8_t Change_Flag = 0;switch(yxypassword.a_Password_Buf){case CHANGE_PASS:   //修改密码printf("开始修改密码\r\n");                                                       //试着删Change_Flag=1;    //修改密码标志位break;case RESET_PASS: //复位密码if(yxypassword.Reset_PossWord() == SUCCESS){printf("复位成功\r\n");                                                       //试着删}else{printf("复位失败r\n");                                                     //试着删}break;case EXIT_LOGIN:    //退出登入  yxypassword.Login_Status = 0;printf("退出成功\r\n");                                                     //试着删break;default:if(Change_Flag == 1)//判断修改密码标志位{//              printf("进入修改密码界面\r\n");                                                       //试着删switch(yxypassword.a_Password_Buf){case PASS_REVOKE:       //撤销输入一位if(yxypassword.Password_Buf_Cnt > 0) //减少计数后直接退出{printf("清除1位\r\n");yxypassword.Password_Buf_Cnt--;}break;case PASS_CONFIRM:   //确认修改密码if(yxypassword.Password_Buf_Cnt == PASSLEN)   //判断是否密码输入完{printf("满值的确认修改\r\n");                                                        //试着删if(yxypassword.Revise_PossWord(yxypassword.Password_Buf) == SUCCESS) //判断修改密码是否成功{yxypassword.Login_Status = LOGGING_IN;        //修改成功,进入已登入状态yxypassword.Password_Buf_Cnt=0;                       //修改成功,清零计数位printf("修改成功\r\n");                                                        //试着删break;}}printf("修改失败\r\n");                                                      //试着删break;case PASS_EMPTY: //清空输入修改printf("清空\r\n");                                                         //试着删yxypassword.Password_Buf_Cnt = 0; //清空输入计数break;default:if(yxypassword.Password_Buf_Cnt >= PASSLEN)       //判断是否密码输入超值,超值丢弃,不超存入{printf("超值舍弃\r\n");                                                      //试着删}else{//把中转站的值赋给缓冲区yxypassword.Password_Buf[yxypassword.Password_Buf_Cnt] = yxypassword.a_Password_Buf;yxypassword.Password_Buf_Cnt++;  //密码数量加1}break;}}break;}
}void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{static uint8_t cnt=0;                                                     //试着删// printf("cnt%d: %d\r\n", cnt, yxypassword.a_Password_Buf);//试着删if(huart->Instance == PASS_UART){if((cnt%2==1))                                                      //试着删{goto yyy;}if(yxypassword.a_Password_Buf>=16)                                                      //试着删{yxypassword.a_Password_Buf -= 6;}switch(yxypassword.Login_Status){case LOGGED_NOT:       //未登入状态Not_Login_in();      //未登入函数break;case LOGGING_IN:       //登入状态Login_in();break;default:break;}//HAL_UART_Transmit(&PASS_UART_HAND, &yxypassword.Password_Buf[yxypassword.Password_Buf_Cnt-1], 1, 10);   //阻塞发送printf("键入:%d\r\n", yxypassword.a_Password_Buf);                                                     //试着删yyy:                                                       //试着删HAL_UART_Receive_IT(&PASS_UART_HAND, &(yxypassword.a_Password_Buf), 1);        //再次开启接收中断cnt++;                                                      //试着删}
}

main.c

头文件包含

主函数代码试用

效果图

登入

框起来的是一个输入,打印出来的

修改密码重新登入

框起来的是一个输入,打印出来的

7 参考资料

STM32CubeMX学习笔记(9)——I2C接口使用(读写EEPROM AT24C02).

AT24C02 Datasheet.

一文看懂I2C协议.

I2C通信协议介绍.

使用cubemx工具的stm32用AT24C02实现简单密码(一点点面向对象的思想编程)相关推荐

  1. 使用cubemx工具的STM32对外部flash(W25Q64)的简单编程

    SPI SPI简介 SPI通信原理 SPI是串行外设接口(Serial Peripheral Interface)的缩写,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯 ...

  2. 发现一个工具,可以清除 xp win7 用户密码,在PE下运行

    发现一个工具,可以清除 xp win7 用户密码,在PE下运行 原版,不是这个,现在都是汉化版,加几个字. 真正的原版,早在几年前就有了.我不记得了. 以前没有这个习惯,写技术日记,记录下来. 现在开 ...

  3. 3款大数据bi工具,让企业数据分析更简单

    ​企业数据可视化的髙速发展趋势让互联网时代的数据分析及可视化拥有全新的面貌.企业针对信息内容的数据分析及可视化,的要求在日益严格,那么有哪些在企业数据分析方面做得好的大数据bi工具呢? 一.大数据bi ...

  4. 用Tkinter打造自己的Python IDE开发工具(1)建立一个简单的代码编辑器

    用Tkinter打造自己的Python IDE开发工具(1)建立一个简单的代码编辑器 很多Python初学者,安装完Python后,已经包含了IDLE代码开发和调试系统.都会再安装各种Python开发 ...

  5. Unity 工具类 之 编辑扩展器 之 简单的音效管理编辑扩展器面板实现

    Unity 工具类 之 编辑扩展器 之 简单的音效管理编辑扩展器面板实现 目录 Unity 工具类 之 编辑扩展器 之 简单的音效管理编辑扩展器面板实现 一.简单介绍 二.实现原理 三.注意事项 四. ...

  6. 拍摄短视频需要哪些工具?一部手机还不够,简单的设备你得有

    拍摄短视频需要哪些工具?一部手机还不够,简单的设备你得有 正所谓,工欲善其事必先利其器,拍摄短视频亦是如此,跟专业的短视频拍摄团队比不了,我们如果只是个人做短视频,又想要做出来一些成绩,那么一部手机其 ...

  7. CubeMX+Eclipse+Jlink STM32开发环境搭建

      Eclipse还是我用得最多的IDE,不管是CCS还是Xilinx的SDK,都是Eclipse的开发环境.所以最近准备做STM32相关的开发时,也准备用Eclipse. 软件下载与安装 STM32 ...

  8. I2C专题(二)—— STM32驱动AT24C02

    前言: 为了方便查看博客,特意申请了一个公众号,附上二维码,有兴趣的朋友可以关注,和我一起讨论学习,一起享受技术,一起成长. github:my github 注:博客所涉及的关于 stm32 的代码 ...

  9. STM32入门必看 简单几步用cubeMX模拟声控灯

    文章目录 一.为什么叫模拟声控灯? 二.操作步骤 1.声音传感模块 2.硬件连接 3.在cubeMX中配置 4.主函数 三.效果展示 一.为什么叫模拟声控灯? 生活中我们可以在很多场所看见声控灯,那么 ...

  10. STM32 cubeMX下配置STM32单片机使无源蜂鸣器输出PWM波并放音乐

    1.首先我们要了解定时器(timer) timer分为三种:高级定时器(tim1,tim8),基本定时器(tim6,tim7),通用定时器(tim2,tim3,tim4,tim5). 基本定时器:它有 ...

最新文章

  1. Java项目:干活管理系统(java+SSM+Jsp+Mysql)
  2. 关闭Bootstrap模态
  3. AntV的花瓣图中鼠标悬浮提示信息去掉与修改
  4. OPMS项目管理系--新增加项目报表统计
  5. 机器学习的理论知识点总结
  6. 【学生选课系统经典】C#与SQLSERVER连接:Windows应用工程案例
  7. linux下tar包安装sudo命令,ubuntu12.04LTS安装gv-412-Linux-x86.tar.gz方法
  8. 快速了解Java集合框架
  9. html玫瑰花效果代码,html5渲染3D玫瑰花情人节礼物js特效代码
  10. 为什么重写equals时必须重写hashCode方法?
  11. SHTML+INC机制说明!
  12. [转载] Python基本语法之:字符串和字典介绍
  13. 同样一句代码,在类内调用,跟类外调用结果不同?
  14. 计算机网络——计算机网络病毒和黑客
  15. java五大框架整理_五大Java常用框架整理!
  16. 3.6计算机网络(网络层概述 电路交换 报文交换 分组交换)
  17. AT070TN92 电源
  18. Android 虚拟按键上报
  19. 麻雀要革命 第三章 宣战!麻雀联盟的华丽出击 第二节
  20. 基于Nginx以及web服务器搭建在线视频播放

热门文章

  1. 基于slate构建文档编辑器
  2. 尚硅谷2022 jenkins教程笔记
  3. python爬虫入门
  4. JSON对象超详细讲解|对象、循环对象、嵌套对象、修改和删除对象属性
  5. foremost命令参数中文说明
  6. 金蝶记账王和易记账哪个好_金蝶易记账和记账王的区别是什么?金蝶易记账的具体操作如下...
  7. win10好用的C语言软件,9款超级实用的Win10软件,一定要收藏,简直不要太好用
  8. 查看dll/exe所依赖的库文件、导出函数、系统位数
  9. bmi计算器公式_bmi计算器免费版客户端
  10. 将图片型pdf转成可选中文字型pdf(免费)