本帖最后由 netlhx 于 2015-11-8 20:35 编辑 9 R2 \% r4 G1 ]7 x, B' ]" ~
" o) a& _( u2 B+ I/ l( tNUCLEO-L476RG到手一段时间了,今天来测试一下硬件I2C的操作,听说很多BUG,试一试看如何。
9 U t7 j f" {; M
+ b6 I8 x' R3 }3 s; {, h准备用I2C来访问AT24C02 EEPROM,将数据写入到EEPROM中,然后再读取出来,验证读写操作的正确性。
* I" |5 h% U+ B4 f& E0 b+ @, S, e$ ]* ?$ X: y) ?7 N" N
硬件平台如下% q2 A& x* @5 y
1 y- D9 w+ |) l1 W+ A/ M, S; ?0 K+ m3 p6 K a9 _
NUCLEO-L476RG) E" L# i% T6 ~% q2 a5 \
2 y0 \1 i \, c; U( x# \
' Q8 s, [. f' }% F- [% P
3 K; i9 m# {" u) l2 @
前段时间FSL的KL02Z套装,上面的MINIDOCK扩展板上恰好有AT24C02,所以借过来用一下,FSL不要生气啊) b# C, `. b6 |2 v
2 M; l8 n; X& o* H9 \8 g% f
& V$ k( P0 G8 s- [2 S+ M: s j( P
! q2 p( V" l1 U4 ^& h; l1 x
标准的ARDUINO接口,放到哪都行。合体照。3 i9 O+ \/ z( F' o- z
* A9 }/ P6 H' G) c+ d8 |0 f
; h& ~( V7 b/ } f
6 e d2 _$ n4 J硬件连接及原理图
3 G4 T, z" [" @+ J' H( \, c& H' }3 N7 @, r1 Y# L* }; I
MINI DOCI上的AT24C02连接
( i8 k+ b Z ^' G
" g( P" `' O4 X2 e ^; S5 S& _0 |( R
& |0 e% V! O3 B& V* {+ f3 e$ Q
5 V& n7 x2 E# bARDUINO接口:SCL及SDA
2 b9 D" i4 z9 [% y: L* Z- M$ ~/ |+ a8 L
7 r Y$ c' z; `3 S1 d
; Y$ b/ h! p- Q; ~6 Y( wNUCLEO-L476RG上对应的接口
" J ?9 f; h/ d$ B4 S% O, ?- e
I* n; H5 k* v
+ L! u& o- U' \/ K( O; w2 F
' T4 z- d$ w7 [) Q. ^4 w
" E" g! _ E4 Y% l3 O' z o) S" ^写程序之前,要了解硬件的基本特性,首先是STM32L476RG上I2C接口支持的特性;其次是AT24C02的特性。& C8 }0 f2 A4 B `
! l$ ?, J- R7 Y- Y) X5 |
STM32L476RG的I2C功能框图如下8 Z, n. F0 c) c
- B* e, d+ m0 Z9 m6 g" p
& J3 `6 ?7 Z' S. j
: d+ n( \9 y6 ?$ u/ C0 A1 c值得一提的是,L4的I2C有自己独立的内核时钟,可以在SYSCLK、PCLK及HSI中任选一种做为I2C的内核时钟。另外L4的I2C支持三种不同的通信速率:100KHZ, 400KHZ,1MHZ。由于AT24C02只支持最高400KHZ,所以这里就选择400KHZ做为内核时钟。1 Y& `9 y7 Z7 i( X. {4 o
7 L0 `' n: g. J* \( \$ R
创建I2C测试工程,建立过程略。几点要注意的地方。
$ s9 ^8 O; Z+ n2 e
) V" u% V( J. t7 m. s5 J J( b首先,本例程中使用的是I2C1,对应的SCL及SDA引脚为PB8及PB9,时钟配置后SYSCLK为80MHZ。) `5 G5 e& w0 m
1 \$ W1 \6 C) s L, k
I2C硬件配置参数如图% C! v2 d" R6 K2 r6 k0 U) q: i
! y1 j! F- @3 r6 `1 x
& S4 G9 r+ ?1 V. \
& k9 C% L( j+ w7 P9 `" G& N: n速度有三种可选:标准,快速,快速加。另外针对不同的特定应用,可以选择设置上升和下降沿的时间,以纳秒为单位,范围为0-120纳秒之间。注意这个TIMING参数,是灰色的,CUBEMX自动计算并设置该值,不像以前的F0和F3系列,需要一个专门的工具来设置TIMING的值,够麻烦的。
. q! P L7 |( l$ p$ D5 Y- [5 s- ?; i3 o* A
下面是使用HAL库与EEPROM通信的主要代码。
4 g2 S; C2 e/ T2 ~) H
/ @# V3 y4 O4 K* p0 f! f: h/ u8 O/ d# t- c& V( p
- /* USER CODE BEGIN PV */
3 }3 I. r" V8 E3 @" X9 z - /* Private variables ---------------------------------------------------------*/) B# o, D2 x, j8 Y6 `5 w
- #define EEPROM_ADDRESS 0xA0( {1 G: q6 M* S- ^8 ?/ O" g5 |
- #define EEPROM_PAGESIZE 0x11* x8 v1 X& Q l8 r$ \2 z) Q
- #define EEPROM_TIMEOUT 1000: Y- y8 m! n7 N$ F
3 v( o% E; E2 h1 y& k- extern I2C_HandleTypeDef hi2c1;. g9 F" v& F& @; @( |$ t
, m( X7 N: \( d* X( W2 d) i- char msg[] = "This is a test!\r\n";
* y k5 M# q1 ? t - ! ?: B( A$ j0 Q5 E& b m; `
- char buf[40];
5 G) M- q: D. i- e - & I8 ?% |6 X) R& ^, o
- int16_t Remaining_Bytes;
/ C$ S; E' K3 }8 K9 s7 G - uint16_t Memory_Address;
. }* b& [+ s6 O - 9 O( C$ J- |- `( N0 P
- uint16_t len;
, ^' b0 a9 g1 q) [; H: d+ b
& e) K$ w7 e0 ~8 x% w- ......
* Q1 ?( R1 o1 }& G/ J - ' w4 e/ Z# F, l: ~
- //write msg to eeprom
* P6 K* m3 q& g8 s) W2 W8 K7 ] - Remaining_Bytes = strlen(msg);
/ \. b$ m# w; T8 D7 m - Memory_Address = 0;
; x# @% j. [( ]6 f( D+ L -
# [" o' Z6 [8 t" A! P3 u# B) R M - while(Remaining_Bytes > 0)
$ z- z# U, m3 a; f - {
# y& ~4 ?% E5 U* [+ i* _ - if(HAL_I2C_Mem_Write_DMA(&hi2c1, EEPROM_ADDRESS, Memory_Address, I2C_MEMADD_SIZE_8BIT, (uint8_t *)(msg + Memory_Address), EEPROM_PAGESIZE) != HAL_OK)
5 n9 C* D. v* b# w) |! w2 C - {) f7 I7 X* C: o; d& c- t0 o% O1 K+ P/ t
- Error_Handler();
' k- ?9 m( _2 ^, w -
/ v1 ]; J# a0 T4 s" O) o9 g* X - }
4 v# \! R' V8 d# I% D& \ - # g. f) `: O; ^3 U' l" p
- while(HAL_I2C_GetState(&hi2c1) != HAL_I2C_STATE_READY)
1 C* A- u5 N2 u. F. ~" } n* z+ Y2 I, d - {
3 N" u" I6 S7 H - }; Y/ o+ T: Q8 m- m4 v
-
2 ?7 R+ y2 I2 N9 I9 k5 H3 | - while (HAL_I2C_IsDeviceReady(&hi2c1, EEPROM_ADDRESS, 10, 300) == HAL_TIMEOUT);
( g0 N3 \ Y. Y9 J9 T D - 0 }( ~3 ^5 h4 C P3 t9 F" v
- while (HAL_I2C_GetState(&hi2c1) != HAL_I2C_STATE_READY)( M$ J- d/ [ p0 |- ~5 A
- {" y( {1 K2 O. k8 }
- }
! r% X5 w& s* ~$ U! q- _( T -
- f) {. `" y1 u7 k; e6 u - Remaining_Bytes -= EEPROM_PAGESIZE;
7 A! \9 ]$ _4 U$ X7 P# ` - & O- J2 V, E8 c
- Memory_Address += EEPROM_PAGESIZE;# z/ ^: R1 _: q+ }: m. ]
- }: t( j* v! j3 Z9 L3 ~
-
1 ]% c2 i; m& ]3 g: G0 L - //read msg to buff0 _1 z$ {5 s7 ]) @
-
8 i5 p' ]' u1 R3 q& ~8 j! j8 s - if(HAL_I2C_Mem_Read_DMA(&hi2c1 , EEPROM_ADDRESS, 0, I2C_MEMADD_SIZE_8BIT, (uint8_t*)buf, strlen(msg))!= HAL_OK)' b/ x- k; [% q7 z ]
- {
5 @4 a6 C6 g6 S# W" c4 l - /* Reading process Error */
. }) O! C/ g# Z; v) D - Error_Handler();
V$ y, \! M5 ]0 Z/ r - }
: I# Z( A q. r0 w3 h -
4 i4 @. V% M* q" d5 F" y; b( V, ^2 ^ - /* Wait for the end of the transfer */ & V( u) V- z2 ]$ h- o, u3 \: ]- L
- while (HAL_I2C_GetState(&hi2c1) != HAL_I2C_STATE_READY)) d7 M# U3 g2 L
- {7 c, i0 p. L$ i) C [
- }
2 ]% p z$ V/ k+ ? u% V -
复制代码 2 { H8 U. Q' ]! Y- x
根据STD库中用户的反馈,I2C出错的原因主要由几方面造成,一是ST设计的I2C接口,通信过程中一些状态变量及标志位的设置非常严格,如果程序逻辑考虑不周全,会导致I2C停止工作,需要软复位后才能继续;另一方面,I2C接口在处理单个,双字节及多字节传输过程中要分开考虑,进一步将问题复杂化了;还有就是结合具体的I2C设备,不同的时序要求,进一步复杂化了通信过程。
7 x, a. l v) ]0 a' R) Z: Z, H
0 I- F7 {. F) |; H" J7 `5 x这些问题在HAL库中都得到了很好的解决,为此,HAL库中设计了几种不同类型的API函数供调用。一类是通用的接收和发送函数,这类API函数将I2C接口视为类似于“流对象”,通信时从流对象中接收或发送数据;另一类则是类似于EEPROM之类的特殊存储对象,从这些存储对象中接收或发送数据时,都需要提供一个地址,以便准确定位。使用EEPROM来实验时,我们使用的是第二类API函数。两个主要的API函数原型及参数如下。
5 x, X* D) i3 e6 k1 P
% _ E4 d7 @' s& h: F& _4 A- /**! R, S5 q' L, y' O' h4 y' R! t) d6 X
- * @brief Transmit in master mode an amount of data in non-blocking mode with DMA
, ?: V7 t1 p' t& Z, \ - * @param hi2c : Pointer to a I2C_HandleTypeDef structure that contains
- u+ l6 E3 A3 x% M' V% y, O - * the configuration information for the specified I2C.- _8 g" [; ^; c% Z
- * @param DevAddress: Target device address
* I* o7 \1 W* f7 z+ B/ Q# { - * @param pData: Pointer to data buffer3 T; A; B+ P" j9 e# r' W. b. v
- * @param Size: Amount of data to be sent3 D; q- }' z' p4 c. Y
- * @retval HAL status
9 l6 a% T; Q. v - */
" F- L: O. x- m2 `( o, c - HAL_StatusTypeDef HAL_I2C_Master_Transmit_DMA(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size)
复制代码- /**
U5 x( ]3 B ]* O( J# z7 v - * @brief Reads an amount of data in non-blocking mode with DMA from a specific memory address.3 e! m" s/ Q, A6 C% K
- * @param hi2c : Pointer to a I2C_HandleTypeDef structure that contains, E+ u3 P4 Q1 i& S/ q$ J) W* T
- * the configuration information for the specified I2C." ?+ h# n1 `1 I/ }) n
- * @param DevAddress: Target device address
1 w- V c- x6 X9 C' y6 S) x1 J - * @param MemAddress: Internal memory address
: j0 q! t; f. t, d; D6 E - * @param MemAddSize: Size of internal memory address
+ ]! p6 _8 ?9 c6 e - * @param pData: Pointer to data buffer
5 ^* l) b4 L4 u2 C' ]0 _ - * @param Size: Amount of data to be read
5 Z& C2 j0 u2 \5 N - * @retval HAL status2 ^8 W" U2 T+ l
- */
5 D7 e* }) E' B- u. h - HAL_StatusTypeDef HAL_I2C_Mem_Read_DMA(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint16_t MemAddress, uint16_t MemAddSize, uint8_t *pData, uint16_t Size)
复制代码 3 `# C. V# t. y; j
关于这两个函数的更详细的调用方法及说明,请参考API文档。
; D0 h7 @& \9 ]0 }0 O# _' Z5 @! k, m
注意在具体操作EEPROM的时候,还要注意,EEPROM分字节读写及页面读写两种类型,对HAL API的调用还要进行适当的修改。
) _) q5 g8 C9 Q4 ?" J |- ]% o1 u) s* p( d' _$ v" L* F$ V: ~
由于ST HAL API做了大量的后台工作,上面的代码看起来过于简单了。但实际上API函数在后台做了大量的工作,这不正是我们期待的么!
) C/ D9 b q! i A6 \ q4 C/ q+ Q: t7 D4 ]+ l3 U
最后来看看运行结果。2 R5 a( p/ [, G3 x3 |
, o+ a3 H' D9 x* q9 j+ J- W+ `UART打印的写入及读取取结果
9 t) c3 V' }* ^/ _" t# g: l
3 w. h5 e6 R6 Q# [3 i( r. `
) H8 `- m2 }# ^7 X
2 {* Y8 {. j& p2 d+ q逻辑分析仪LA/ H9 I, Q' A( m4 x: u! T) {
" _& A L5 V; k% B2 B0 c- i
% ^/ v- g7 Y/ b' s5 D( d0 ^. G
# L) _; h# h9 rI2C通信全程,LA分析结果: b! \% B9 W2 v* Q( s
2 J$ b0 N& [, Q! e9 _
4 ]! m6 O, ~; i
, F! r9 m' m v9 k6 S/ |+ ^* f c开始通信,顺序依次是开始信号,地址,EEPROM写入地址,第一个数据字节* R& b: B- O7 l6 I" ~* n8 A- w
; Q/ Q/ `7 u2 Z, ]" S* C
+ R1 ^% N. Q' J; B
- k$ F9 p0 E% h, b$ }4 {3 |
字符0x0D, 0x0A, 结束信号。* a2 L9 }1 N; U$ P/ r) ^
0 }5 S! {& z1 o, T, N
$ W; Q* A7 B8 m: l* t# w
Y( P# Y0 F3 [( K接收开始,顺序依次是开始信号,地址,EEPROM读取地址,第一个数据字节: k. m6 F+ c1 P4 F7 r S, ?7 n& j7 H
% A8 z: n, c% \
' Q% F, j* H" t& @" x' F
- [- |1 `# q; p7 Y) Q" K5 \读取结束
C3 W6 e/ T' }0 w9 \- e- m+ p0 L3 \9 A& {1 S: G
2 c$ ]2 J7 | |7 k- p0 @
* s" u1 n& W8 V/ v2 j9 S1 z% t$ f7 _$ R" X8 b2 t) i. L/ u
4 E+ R" I* f9 R& n+ J/ k$ V* N附工程
( h/ U$ O5 ^0 p; }! ~9 S, G! }- }7 }+ k
6 M; C* ~; R2 e7 T/ Y
eeprom.zip
(3.17 MB, 下载次数: 201)
|
{
Error_Handler();/ M, g# p2 [3 ?7 ~& M/ H7 `/ E0 Y# L
};程序直接进入Error_Handler()函数,请问是什么原因呢?我检查了很多地方,还是不行,求指教,非常感谢!(测试环境stm32l476rg+at24c02,后来用了at24c16测试还是失败)
1、移植,直接秒杀不用多说
2、效率,IIC的硬件通讯过程中用了太多的访问标志位,实际效率不会增加,一样是让CPU在耗,
所以,效率反而没有模拟IIC高,因为GPIO的反转速率轻松满足IIC的通信速率,所以0 k3 n9 G" I0 m6 Z/ i. W& p
模拟IIC照样更胜1筹。
3、复杂性,ST的通病,反而模拟IIC更加把协议变得很直观。! S5 v$ ?. a1 G. R% e
纵上所述,感觉这个硬件IIC就是个鸡肋。
梦源的牌子吧,100M