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限制
. B- P/ p. p. e1 O
, u8 U; T' p) a9 W2 w* P# y$ c8 i⏩ I2C协议层:I2C总线在传送数据的过程中共有三种类型信号,在这些信号中,起始信号是必需的,结束和应答信号可以不要;I2C总线寻址按照从机地址可分为7位和10位(寻址字节)寻址两种,D7~D1位组成从机的地址,D0位是数据传送方向(0表示主机向从机写数据/1表示主机由从机读数据)
& Q x! @) Y( m; X( C3 s) h, W+ t2 \/ f3 T& y$ P0 u
开始信号:SCL为高电平时,SDA由高电平向低电平跳变,开始传送数据 结束信号:SCL为高电平时,SDA由低电平向高电平跳变,结束传送数据 ! a9 ~8 ^ [% s9 G1 v
应答信号:接收数据的IC在接收到8位数据后,向发送数据的IC发出特定的低电平脉冲,表示已经收到数据。CPU向受控单元发出一个信号后,等待受控单元发出一个应答信号,CPU接收到应答信号后,根据实际情况作出是否继续传递信号的判断。若为收到应答性,判断为受控单元故障 3 r v, H4 G# W' d! B8 \
R' f6 m( T. m" t
" m7 V2 U* C) u- L) l! q6 K% U' b$ C7 u ?9 m' R
1.2 AT24C02芯片简介 AT24C02是一个2K位串行CMOS,内部含有256个字节,此芯片具有I2C通讯接口,芯片内保存的数据在掉电的情况下不丢失(EEPROM),常用于存放比较重要的数据。本实验使用的是SOP-8封装的AT24C02芯片,其引脚说明见下图 9 r" j/ e/ o3 `" @, s
% x4 c4 Z7 Z+ c. G# z
AT24C02芯片的器件地址为7位,高4位固定为1010,低3位有上表中的A0/A1/A2引脚的电平决定,还有一位(最低位R/W)用来选择读写方向。本实验中A0/A1/A2引脚接在GND上了,因此器件地址为1010000;加上最低位的读写方向位后,写器件地址为10100000(0xA0),读器件地址为10100001(0xA1) 4 ]1 G* n1 ]; u2 f& o" S4 ?
' a$ c8 g/ l- m8 J' L
- J8 e+ g: W2 I9 ?. ^下图为AT24C02的总线时序图和时间参数 4 k1 I, _2 G' Q% j! [
" i8 M/ c6 b" F. _0 ]' }
5 L/ r/ e5 X" D* ]; `1 b" J3 G( i( x7 T5 E1 @
2.硬件设计 D1指示灯用来提示系统运行状态,K_UP按键用来控制24C02的数据写入,K_DOWN按键用来控制24C02的数据读取,数据的写入与读取信息通过串口1打印出来
0 z. r5 b' F0 L% D3 j. L2 D3 y+ o5 w2 |$ K
指示灯D1 USART1串口 AT24C02
) f0 z( {1 B, O3 YK_UP和K_DOWN按键9 h0 U( K \7 _) c: K# S
' n+ F: @8 d2 F$ Z# d& ~
+ ?6 i% C- Y9 o, g: C2 C, C 5 ?9 n8 h2 ?" k2 r6 x' ~1 S
% {1 L& S) p) a! n1 `- Q
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,生成工程代码
5 P8 i* s' ?# ~, q8 o* K% Q
" v% n2 h- S0 g
3.2 MDK-ARM软件编程 ➡️ 在i2c.c文件下可以看到I2C初始化函数 - void MX_I2C2_Init(void){9 A; ~8 v/ D2 `2 I' X
- hi2c2.Instance = I2C2;" W2 Y6 u3 g" }) W- o- _/ b
- hi2c2.Init.ClockSpeed = 100000;% `1 g: X1 K2 m, v+ ] q. U
- hi2c2.Init.DutyCycle = I2C_DUTYCYCLE_2;
6 u3 C0 _1 v& Z3 T0 b2 k - hi2c2.Init.OwnAddress1 = 0;4 D/ e% G1 N% @" \" ~
- hi2c2.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;- U' E: M" |' J. U8 f1 Y
- hi2c2.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
( Q5 s6 b& \3 Z. F U2 T - hi2c2.Init.OwnAddress2 = 0;
+ U: J5 f' \+ x+ C! r - hi2c2.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
$ n. P8 ]$ j7 c% v) G+ O - hi2c2.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;' c# f) g) {% A; M5 V
- if (HAL_I2C_Init(&hi2c2) != HAL_OK){
/ i( K2 Y2 |6 V3 C" v/ w! ]' O3 k - Error_Handler();1 d8 U Q( a; E) U
- }: I/ k3 Y1 q1 _3 N
- }6 J! w8 T- I1 A( j3 n. I; P
- # O; S" d5 T8 R, j
- void HAL_I2C_MspInit(I2C_HandleTypeDef* i2cHandle){& Z% w2 z0 Z) Y
- GPIO_InitTypeDef GPIO_InitStruct = {0};& P' v4 A5 I u
- if(i2cHandle->Instance==I2C2){9 x9 Q, k' p6 r/ O$ W
- __HAL_RCC_GPIOB_CLK_ENABLE();
, _! l2 `) O: _6 B% b7 G - /**I2C2 GPIO Configuration
$ `- Y! K2 D5 N) T' B3 ]9 B8 ?) I - PB10 ------> I2C2_SCL
- F2 S6 r- u/ J* N - PB11 ------> I2C2_SDA*/1 b; E' I8 f& N3 K1 m# Y& M
- GPIO_InitStruct.Pin = GPIO_PIN_10|GPIO_PIN_11;
6 y% R) z9 J( _2 f; {1 p - GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;* X& i( P/ M: T8 f
- GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;, x" W4 k: n* }
- HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);5 c q0 \0 b5 p9 ]7 }6 y5 [- W
- /* I2C2 clock enable */
5 i1 a% T1 z: D# {1 k4 u) z - /*网上看到很多资料说此处的I2C时钟初始化函数应该放在GPIO
! ~$ |3 n2 B! ?# n4 ~ - 初始化之前,但是这里没有调整也能够正常读写EEPROM*/
3 p0 a5 s9 o% r - __HAL_RCC_I2C2_CLK_ENABLE(); 4 V7 n. \0 i2 e7 u* Q4 J
- }
复制代码➡️ 创建按键驱动文件key.c 和相关头文件key.h,参考[color=var(--weui-LINK)]按键输入例程 ➡️ 创建AT24C02驱动文件24cxx.c和24cxx.h - #define ADDR_24CXX_WRITE 0XA0
$ Q) C0 U- j& ]+ |9 N) A5 M - #define ADDR_24CXX_READ 0XA1 Z- E) R5 b3 X
- void AT24CXX_Init(void){ z& k" _' }. i% t% p
- MX_I2C2_Init();
+ n1 Q; F$ x* G& ?2 ~: B - while(AT24CXX_Check()){: @* ?. j% t$ l# |8 e
- printf("AT24C02 Checked Failed!\r\n");
* Y% q. Z M6 x - HAL_Delay(500);
6 @# t# q5 r- ~+ Z3 r. _ - }
, ]6 h6 G: H# |* ~7 y - printf("AT24C02 Checked Sucessed!\r\n");
8 z4 ]! j6 e( D% S3 L' l" V' k: J4 k - }
6 d: U6 j% _4 v) g - ; g7 Y" t' K% X* O9 e
- uint8_t AT24CXX_Check(void){
- g/ ~' J8 O; S. @0 W3 J - uint8_t temp;7 H) t9 A% z# T# N
- HAL_I2C_Mem_Read(&hi2c2,ADDR_24CXX_READ,255,I2C_MEMADD_SIZE_8BIT,&temp,1,0xff);
7 n; L3 q( p, Z% G2 [ g - if(temp==0x36)
! ~- r" }7 @' ]/ y a) ]0 G2 G. G - return 0; ' n1 \1 g$ J' ^8 O4 m$ D
- else{
! Z! ~+ U: f4 c6 C# B - uint8_t data = 0x36;
1 R8 Q# ` }+ H6 x - HAL_I2C_Mem_Write(&hi2c2,ADDR_24CXX_WRITE,255,I2C_MEMADD_SIZE_8BIT,&data,1,0xff);
; l) K+ x1 r5 E$ x4 m" o. C - HAL_I2C_Mem_Read(&hi2c2,ADDR_24CXX_READ,255,I2C_MEMADD_SIZE_8BIT,&temp,1,0xff);
( M9 v# U1 U0 J. d - if(temp==0x36)
3 ^* d( L# p$ a: P+ y+ \: l1 K$ z1 Y - return 0;
( d+ T @: Y, }: w3 g2 B - }
$ ^8 Q( C: V: Z; c/ M) b4 K) t - return 1; 7 F& q* f1 W+ p/ r0 T; y6 c8 @
- }
复制代码
! g' N) y( y6 ~# Y/ R: B
➡️ 在main.c文件下编写I2C测试代码 AT24C02的2Kbit分为32页,每页8个字节。而EEPROM也可以按页写入,本例使用了按页写入的方式,分32次写入。注意每次写入完毕需要延时5ms,是AT24C02芯片的要求;读取数据没有页的限制,可以一次全部读取256个字节 - /* USER CODE BEGIN PV */
, F1 K! X! e- ^2 Z9 b; _- b* ^ - #define ADDR_24CXX_WRITE 0XA0/ D0 k3 {- b8 y2 m ]$ m
- #define ADDR_24CXX_READ 0XA1
( G L! g1 ^1 o' n r6 W - uint8_t WriteBuf[256];
3 [( [3 f9 `" i. r) J, } - uint8_t ReadBuf[256]; ?& v8 @/ M: e. M$ Q( s1 y
- uint16_t i,j;0 t4 F" ^$ _' }* N
- /* USER CODE END PV */
1 U% M% \: U2 I* F: d# y* ~+ ` - int main(void){
/ V; x6 G6 z. e2 z8 U - uint8_t key;
; u6 O$ `# ~* n; c& Z5 n j: Q6 b: D - HAL_Init();# b6 k' b( ?3 H0 R( ]
- SystemClock_Config();/ E3 r: p' \' u4 ?
- MX_GPIO_Init();% d$ k& F) C; r. {' ~5 S
- MX_I2C2_Init();
; S0 j3 s G2 D& L2 W - MX_USART1_UART_Init();; O8 b5 n1 Q6 x) p. ^5 `7 [
- /* USER CODE BEGIN 2 */
0 H* C- S/ Y2 u1 a2 d - AT24CXX_Init(void);, j9 E K+ D9 d; A" Z# I! }
- printf("\r\n*********STM32CubeMX I2C AT24C02 Example*********\r\n");% {2 U# L/ R6 Z7 f' N1 H
- for(i = 0;i < 256; i++){ //初始化写数据缓冲区
1 |$ ?3 Y4 N8 |2 h' {) o - WriteBuf[i] = i;
, @1 m& u, N6 n- U/ y) k - }
6 H1 q, E w) M7 i - /* USER CODE END 2 */
1 N& w9 Y' J% d X4 v - while (1){: b' k2 T6 P' G+ D& h1 C
- key = KEY_Scan(0);
/ u! n) x3 p2 Z0 I0 V - if(key == KEY_UP_PRES){
" Q, c7 N( b* {, E2 P1 ^ - for(j = 0;j < 32;j++){ //按页写入EEPROM,分32次写入
& ]( [5 T. \5 S0 R# g [- I - if(HAL_I2C_Mem_Write(&hi2c2,ADDR_24CXX_WRITE,8*j,I2C_MEMADD_SIZE_8BIT,WriteBuf+8*j,8,0xFF) == HAL_OK){6 K7 I/ Z6 c% U* X9 w) n' V' _
- printf("\r\nEEPROM 24C02 Write Test OK!\r\n");$ U: n7 e. v& t
- HAL_Delay(5);
+ t. g8 R* _8 f9 S( }1 `: ^ - }
' A. ?+ A1 K4 D* b8 O3 f; @ - else{) b) }. W) L$ \1 @! g
- printf("\r\nEEPROM 24C02 Write Test False!\r\n");
0 N, U1 B2 p3 k5 e" } - HAL_Delay(5); & @9 S) g P) I' _# {+ e
- }5 S" |( ~; R3 Z8 w# E5 k
- }- k( z5 s9 M' e' ~6 |) }/ `9 k
- }& t5 p3 h' h. t4 K) `5 i0 C P! ]5 h
- //EEPROM读取没有页限制,可以一次读取256个字节 5 Z+ _; h8 Y' C }; E! H* c
- if(key == KEY_DOWN_PRES){
0 S- d3 N0 v5 P, A+ t$ P. ~0 X - HAL_I2C_Mem_Read(&hi2c2,ADDR_24CXX_READ,0,I2C_MEMADD_SIZE_8BIT,ReadBuf,256,0xFF);
- G* E O, u+ E* v# q& C0 ? - for(i=0;i<256;i++){2 @& R$ U- Z g) d6 e. g1 X
- printf("0x%02X ",ReadBuf[i]);. I0 f# N8 J8 Z
- }
6 Z4 E# b3 C2 \8 `: Y; [ - //通过内存比较,判断读取和写入的数据是否相同
' b5 p5 G0 Q0 ?$ H - if(memcmp(WriteBuf,ReadBuf,256) == 0){ ' Q2 R" E% F! B7 e# J+ X' D6 H" }
- printf("\r\nEEPROM 24C02 Read Test OK!\r\n");
9 x( |4 H; ?% B" f - }" _) ^) ~* b& j. s* O
- else{, C, ?: T8 g G4 E' s
- printf("\r\nEEPROM 24C02 Read Test False!\r\n");
1 r% T- M# G5 S$ d+ E' i" g - }
2 }! f' P* [7 ^& u- I0 R9 X) P - }
8 B; \9 M+ d; z8 f' m2 N" V2 g -
8 { p! e! p+ ]2 T - HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_0);
8 O, V ^1 |/ P& L/ f9 F - HAL_Delay(500);+ f4 ?+ m. \$ u4 T# e
- }
. L; I: Z: _3 p0 g - }
复制代码 - H) {( B0 i1 M7 h" D8 {# |4 u! I
4.下载验证 编译无误下载到开发板后,可以看到D1指示灯不断闪烁,当按下K_UP按键后数据写入到24C02芯片内,当按下K_DOWN按键后读取24C02芯片的值,同时串口打印出相应信息
y, i/ q% ?1 u/ C! O+ B. L7 p# H7 Z" B$ v' j; ]6 O- l1 {
0 c6 N" T- w( o, a E5 M2 Z2 g: v转载自:嵌入式攻城狮 1 g! k/ u) a/ U$ F
8 J2 A$ S# s/ X9 A) N
如有侵权请联系删除* I8 p: ?: E* r
' w8 P. l% X$ @: n8 l& S/ U3 J% g9 G6 {
|