|
1.I2C总线及AT24C02芯片 1.1 I2C总线简介 I2C(Inter-Integrated Circuit)总线是由PHILIPS公司开发的两线式串行总线,用于连接微控制器及其外围设备。是由数据线SDA和时钟线SCL构成的串行总线,可发送和接收数据。在CPU与被控IC之间、IC与IC之间进行双向传送,高速I2C总线一般可达400kbps以上。下面从物理层和协议层两方面来了解I2C: ⏩ I2C物理层:可连接多个I2C通讯设备,支持多个通讯主机和从机;每个连接到总线的设备都有一个独立的地址,主机利用这个地址进行不同设备之间的访问;总线通过上拉电阻接到电源,当I2C设备空闲时会输出高阻态;多主机同时使用总线时,利用仲裁方式觉得由哪个设备占用总线;有三种传输模式,标准模式(100kbit/s)、快速模式(400kbit/s)、高速模式(3.4Mbit/s 多数I2C设备不支持);连接到相同总线的I2C数量受最大电容400pF限制
5 _- Y2 L7 o9 R5 s \ $ u: D0 c# x6 O# y: s
⏩ I2C协议层:I2C总线在传送数据的过程中共有三种类型信号,在这些信号中,起始信号是必需的,结束和应答信号可以不要;I2C总线寻址按照从机地址可分为7位和10位(寻址字节)寻址两种,D7~D1位组成从机的地址,D0位是数据传送方向(0表示主机向从机写数据/1表示主机由从机读数据) " S; Z3 m2 y c5 F. q: r @7 {5 n
- s0 f! ^( G" }8 L4 Q' \2 `/ l# t
开始信号:SCL为高电平时,SDA由高电平向低电平跳变,开始传送数据 结束信号:SCL为高电平时,SDA由低电平向高电平跳变,结束传送数据
2 Q% q" `! J6 l应答信号:接收数据的IC在接收到8位数据后,向发送数据的IC发出特定的低电平脉冲,表示已经收到数据。CPU向受控单元发出一个信号后,等待受控单元发出一个应答信号,CPU接收到应答信号后,根据实际情况作出是否继续传递信号的判断。若为收到应答性,判断为受控单元故障
9 w0 Y: i+ r+ A9 W6 |& J$ R7 X% o8 T& O; `2 Z5 {, M& P- I
) T+ j1 Y4 z9 v( J
, x. D2 d# H1 `8 V y4 c
1.2 AT24C02芯片简介 AT24C02是一个2K位串行CMOS,内部含有256个字节,此芯片具有I2C通讯接口,芯片内保存的数据在掉电的情况下不丢失(EEPROM),常用于存放比较重要的数据。本实验使用的是SOP-8封装的AT24C02芯片,其引脚说明见下图 + A( |7 {# |6 F1 C/ t1 @
1 |9 v' [" w; n9 s$ I
AT24C02芯片的器件地址为7位,高4位固定为1010,低3位有上表中的A0/A1/A2引脚的电平决定,还有一位(最低位R/W)用来选择读写方向。本实验中A0/A1/A2引脚接在GND上了,因此器件地址为1010000;加上最低位的读写方向位后,写器件地址为10100000(0xA0),读器件地址为10100001(0xA1)
$ ?! T- V, @) C; Q7 T9 I: y8 h
6 b! J- Z1 `7 C3 `. C& h# V$ z2 y; |" P6 ]! j2 c, X9 s
下图为AT24C02的总线时序图和时间参数
. g' N* z( J" z # o- v Z" n( X% p
3 S) T, I1 Z3 u
6 E) X- b) [/ f2 G! K
2.硬件设计 D1指示灯用来提示系统运行状态,K_UP按键用来控制24C02的数据写入,K_DOWN按键用来控制24C02的数据读取,数据的写入与读取信息通过串口1打印出来
5 v% @- i5 A; A3 k
# V" O) o+ z- p- d4 L指示灯D1 USART1串口 AT24C02
# ?, w! x% n( D8 IK_UP和K_DOWN按键* l$ q+ \; @2 \% P, ]8 n
- W5 i2 g0 F7 _" F4 e x+ l" q1 H) q# ]. n
1 x$ Q; _% d2 E4 [9 b C
( O0 F, I6 X V2 A4 u9 _; A6 x9 f5 b0 \
3.软件设计  3.1 STM32CubeMX设置 ➡️ RCC设置外接HSE,时钟设置为72M ➡️ PC0设置为GPIO推挽输出模式、上拉、高速、默认输出电平为高电平 ➡️ USART1选择为异步通讯方式,波特率设置为115200Bits/s,传输数据长度为8Bit,无奇偶校验,1位停止位 ➡️ PA0设置为GPIO输入模式、下拉模式;PE3设置为GPIO输入模式、上拉模式 ➡️ 激活I2C2,选择标准传输模式,选择7位寻址地址,其余默认设置 ➡️输入工程名,选择路径(不要有中文),选择MDK-ARM V5;勾选Generated periphera initialization as a pair of ‘.c/.h’ files per IP ;点击GENERATE CODE,生成工程代码 6 h$ S- O" Q' |! b" u
$ _2 m9 |# G) y+ u$ \( |4 d1 u5 w% S
3.2 MDK-ARM软件编程 ➡️ 在i2c.c文件下可以看到I2C初始化函数 - void MX_I2C2_Init(void){* a" I% E8 n4 A1 @, X
- hi2c2.Instance = I2C2;
- W9 E+ o; ^- m2 L0 N; N ] - hi2c2.Init.ClockSpeed = 100000; R0 u+ C- T3 B) R- d6 L- G
- hi2c2.Init.DutyCycle = I2C_DUTYCYCLE_2;
?$ O! b8 ~8 p - hi2c2.Init.OwnAddress1 = 0;1 b+ L: w% k6 k. `, l$ s) |$ k
- hi2c2.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
. n: t4 X, @& p0 N$ A* c: E - hi2c2.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
0 A/ }: }2 G4 C( C2 A: l; h2 X8 [% ^4 R - hi2c2.Init.OwnAddress2 = 0;
2 X3 Q0 _) r V2 v6 A - hi2c2.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;, J* o' z, q0 T# j( S
- hi2c2.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;: O/ Z7 E6 `, W4 ~* u {% q
- if (HAL_I2C_Init(&hi2c2) != HAL_OK){) G/ L/ J4 T! t+ Z6 Y; x& c
- Error_Handler();6 c) X# V, W' @# M. Z
- }
$ P5 V- o. l6 k! A - }
* g/ U' J& X4 n/ @( N* o
- F I5 q; I! I% E& j1 P2 c3 Z* ~8 N- void HAL_I2C_MspInit(I2C_HandleTypeDef* i2cHandle){
( |" L4 y1 F+ l" \4 ]$ z0 W - GPIO_InitTypeDef GPIO_InitStruct = {0};
# R* _5 F" w- L# U6 v9 Y6 G% x- Y - if(i2cHandle->Instance==I2C2){
5 d/ g) [. i) E1 m/ L6 d - __HAL_RCC_GPIOB_CLK_ENABLE();
) v, g( |" \& B( X. X- W/ r - /**I2C2 GPIO Configuration
. L+ r ?7 s, M! Y C) K - PB10 ------> I2C2_SCL8 D" u6 _/ h: n9 ?6 M
- PB11 ------> I2C2_SDA*/ |$ ~; r+ U, K$ y M9 c
- GPIO_InitStruct.Pin = GPIO_PIN_10|GPIO_PIN_11;1 o. z& d) I: L# v# @+ Z4 Z& P
- GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;4 Q8 R4 R* y& B @2 @2 d! p
- GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;2 `+ W) W' O6 \0 Y
- HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
5 b) H2 A' K% [+ ^3 ~ - /* I2C2 clock enable */
. y- V+ o& w5 E" ]) V; M. p9 j - /*网上看到很多资料说此处的I2C时钟初始化函数应该放在GPIO/ o$ X$ ]- n2 C3 w# Q: y
- 初始化之前,但是这里没有调整也能够正常读写EEPROM*/
) A! g9 ?2 b* q# e9 n0 Z - __HAL_RCC_I2C2_CLK_ENABLE(); 2 M6 O; v# _( J9 ?
- }
复制代码➡️ 创建按键驱动文件key.c 和相关头文件key.h,参考[color=var(--weui-LINK)]按键输入例程 ➡️ 创建AT24C02驱动文件24cxx.c和24cxx.h - #define ADDR_24CXX_WRITE 0XA0
! z- ^# V5 D- M" X# v& s7 Y - #define ADDR_24CXX_READ 0XA1( c( ]9 y- ]. x7 L, r
- void AT24CXX_Init(void){
/ U! x9 M3 G1 \8 L- U Z - MX_I2C2_Init();
& @9 [% C4 E4 {: m' [1 A; K( C - while(AT24CXX_Check()){- s* Q7 ^2 r+ o" `) @( Z
- printf("AT24C02 Checked Failed!\r\n");6 Q0 M# O, O: r6 L$ d8 `7 j% w
- HAL_Delay(500);
5 J0 ?8 E; G- C6 {) } - }, _) P# e8 O- V U5 U
- printf("AT24C02 Checked Sucessed!\r\n");
( v- o( o! L1 o3 B- l3 H( a' A - }6 [* @* a" z# M1 Q6 X) }: f! c
- * {9 B) B5 p9 g3 N0 s
- uint8_t AT24CXX_Check(void){6 \" y3 A( e9 l5 ?# ` W3 R9 ]
- uint8_t temp;: F o0 } A# O' y; [. s6 o1 ]
- HAL_I2C_Mem_Read(&hi2c2,ADDR_24CXX_READ,255,I2C_MEMADD_SIZE_8BIT,&temp,1,0xff);
: C8 y2 Y+ |7 @8 F/ G$ T - if(temp==0x36)
' \1 |1 o+ I4 A2 T4 {! F - return 0; , A! D9 s/ M7 D& ?
- else{
3 U/ b8 \# M8 ~9 r4 j - uint8_t data = 0x36;1 h* S) f* K$ ^! f; y, O; \
- HAL_I2C_Mem_Write(&hi2c2,ADDR_24CXX_WRITE,255,I2C_MEMADD_SIZE_8BIT,&data,1,0xff);
3 J$ i/ N# X2 N: ~( Q; d& n/ o: @ - HAL_I2C_Mem_Read(&hi2c2,ADDR_24CXX_READ,255,I2C_MEMADD_SIZE_8BIT,&temp,1,0xff); . f0 x' s- A- y
- if(temp==0x36) * t0 M( ^' m: O- b8 i: p% I6 E
- return 0;4 E* c8 | o! |9 N
- } g1 y. L! ~: T, R8 H7 k
- return 1;
' v Z, l2 M, J2 f) m$ { - }
复制代码
4 D5 U* }* |+ X- G5 k7 i
➡️ 在main.c文件下编写I2C测试代码 AT24C02的2Kbit分为32页,每页8个字节。而EEPROM也可以按页写入,本例使用了按页写入的方式,分32次写入。注意每次写入完毕需要延时5ms,是AT24C02芯片的要求;读取数据没有页的限制,可以一次全部读取256个字节 - /* USER CODE BEGIN PV */; l. Z# j$ w0 e9 D0 k* Y% ?* A
- #define ADDR_24CXX_WRITE 0XA0# [8 m: s6 g- Q: h
- #define ADDR_24CXX_READ 0XA1$ C' f# p3 M8 v5 b8 N
- uint8_t WriteBuf[256];1 p3 R; z& w( i
- uint8_t ReadBuf[256];
" M2 F# `( k7 V% J! S1 J. h2 g - uint16_t i,j;
* W! ~- Y3 j" X) v9 e - /* USER CODE END PV */! x( Q9 B1 S. V4 {! R
- int main(void){8 F8 H7 B& A: L. q
- uint8_t key;
4 F: d% y4 [* X9 x" F - HAL_Init();; u9 j! @. o1 O; E1 E* M; c+ h. |6 v
- SystemClock_Config();
7 g' n$ C* \/ Z% Q7 ` - MX_GPIO_Init();! b& `( Y8 z( P! n% C: q
- MX_I2C2_Init();3 E, L" T7 G1 x$ H2 Y# }) x) o& E0 h. ?. A
- MX_USART1_UART_Init();
, L( y6 j7 E2 u/ R7 G$ ]- s1 s - /* USER CODE BEGIN 2 */
# `# s/ m& `* K - AT24CXX_Init(void);/ K" j& E' p' g5 J
- printf("\r\n*********STM32CubeMX I2C AT24C02 Example*********\r\n");
$ C9 Z& S; k( q/ z5 Q$ C5 }7 E - for(i = 0;i < 256; i++){ //初始化写数据缓冲区
( {* }8 r5 Z8 w! y/ P - WriteBuf[i] = i;, U5 ^3 b8 p* m8 |
- }0 Z/ I# {" A3 a! H) I1 q4 u: c" L
- /* USER CODE END 2 */- A) S( r/ t5 e x* t
- while (1){
4 t& W& U9 K! s7 H) y, N - key = KEY_Scan(0);
7 @( R- x+ _, G4 Q - if(key == KEY_UP_PRES){
4 y6 ^4 |# `# x: t8 [3 s# t; }; O* k+ B - for(j = 0;j < 32;j++){ //按页写入EEPROM,分32次写入
9 d3 {4 o0 q3 |0 h - if(HAL_I2C_Mem_Write(&hi2c2,ADDR_24CXX_WRITE,8*j,I2C_MEMADD_SIZE_8BIT,WriteBuf+8*j,8,0xFF) == HAL_OK){
# F* v! P- i( w7 H1 r4 M5 d& |: z: g - printf("\r\nEEPROM 24C02 Write Test OK!\r\n");
! t4 [ `$ T2 s& l' [ - HAL_Delay(5);) @1 ]$ {3 W& @+ d- m3 j2 y
- }
; |/ C/ w* B; [1 V$ X2 V0 A' H4 z - else{$ @2 e8 W+ N2 T b. x
- printf("\r\nEEPROM 24C02 Write Test False!\r\n");: ~; Z+ ~0 g& Q% o6 j% [
- HAL_Delay(5);
+ J4 l+ D! S3 \1 C - }7 {2 l2 b4 ^( m1 V
- }
% r* _3 X% d7 V" n6 z8 Y - }, O+ P$ q2 O9 _& m- S2 _$ \6 X: i
- //EEPROM读取没有页限制,可以一次读取256个字节 1 H5 G& U5 I, g" X" D: P! @" l
- if(key == KEY_DOWN_PRES){ , F, V3 K+ h3 f% g
- HAL_I2C_Mem_Read(&hi2c2,ADDR_24CXX_READ,0,I2C_MEMADD_SIZE_8BIT,ReadBuf,256,0xFF);6 c. E9 _$ a% o- z \+ Z; o
- for(i=0;i<256;i++){ Q; A& {3 o0 }( R, f0 t" d& f! C
- printf("0x%02X ",ReadBuf[i]);
& t/ Q/ f1 C4 {9 d: u# P9 P$ r% j - }& J7 ], }6 D/ e. l6 ?
- //通过内存比较,判断读取和写入的数据是否相同 u! k1 \6 Z- K9 K5 C l
- if(memcmp(WriteBuf,ReadBuf,256) == 0){
4 Z- S/ a" ^& z4 D- F' T - printf("\r\nEEPROM 24C02 Read Test OK!\r\n");2 o+ J' N: D# P2 W8 U" D
- }1 u/ p6 B3 f. ~+ ^
- else{! {6 r {9 O( H, p3 [$ e
- printf("\r\nEEPROM 24C02 Read Test False!\r\n");
% m! \8 f( ]. j, f H ?9 h - }8 { f. I7 j, E: j h
- }- J2 G* k' A* _. s" K: R/ A+ z
- ) {2 h: O H1 Q1 {, i' C$ F8 i+ O
- HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_0);
0 A' g5 E* [/ ~8 a: M+ h: C - HAL_Delay(500);# T/ e6 l7 @" p3 r! a0 d5 t9 n0 U
- }! `$ \/ i& h1 P0 @0 x' \
- }
复制代码
, Q$ N v X# V+ G2 l" w5 c
4.下载验证 编译无误下载到开发板后,可以看到D1指示灯不断闪烁,当按下K_UP按键后数据写入到24C02芯片内,当按下K_DOWN按键后读取24C02芯片的值,同时串口打印出相应信息 : E, I, P# z! B2 w( P8 x( Y
1 \% p# S! M0 h2 H
/ C8 [# Z Q/ O+ q2 P转载自:嵌入式攻城狮
* ?3 n$ ~) g' Q- }$ R ' G/ G" B @- G8 D/ j3 I
如有侵权请联系删除
i$ z' j" x. g0 V ' f8 P2 ~8 B1 ]0 C: c
8 K4 r) m. `, e2 o8 G; x# t" L
|