本帖最后由 netlhx 于 2015-11-8 20:35 编辑 1 L8 u# }5 }) N! S! q) h; R- l
8 ~5 u+ u. M, h( B( U& R) F& {NUCLEO-L476RG到手一段时间了,今天来测试一下硬件I2C的操作,听说很多BUG,试一试看如何。
' `( |" m3 _% ]( Z& n- A
: {: m+ v' O- q0 E% q准备用I2C来访问AT24C02 EEPROM,将数据写入到EEPROM中,然后再读取出来,验证读写操作的正确性。
) e3 Z$ X7 i( s' h }# P$ D7 c% B2 _. R, e2 H
硬件平台如下% l" w( P) @" i; b* w" A0 P
! j/ q( s5 T9 @' A9 g5 E- g3 n: q
9 q' D8 o( k( w" H+ e$ Y/ m9 @NUCLEO-L476RG
5 ~; V+ Q4 I5 C$ t9 x8 l! P
8 T' S' G" P) D; h7 ^
# \0 l- [8 V9 h) A5 j5 g
) D. l/ C" t# p: K( w
前段时间FSL的KL02Z套装,上面的MINIDOCK扩展板上恰好有AT24C02,所以借过来用一下,FSL不要生气啊
$ f6 T1 V, c$ y4 O8 ?2 N, P! C1 D3 X8 Y0 d1 N& S
! O" a, x+ e" j# s9 n6 R' e
+ k6 f- m. w9 d! @4 d标准的ARDUINO接口,放到哪都行。合体照。6 a: x1 ]1 ^2 s/ y; J) ^
r; ]: N5 i8 [
* A0 F8 g& Q( K0 A1 K1 u- p I& M
3 R3 D! B1 z% |6 b$ }* j- x4 l
硬件连接及原理图1 k% D: ]" M6 `/ z
( N# H% N" B0 N' RMINI DOCI上的AT24C02连接
. V) v. r& C# ]- i( x& ? Z
# T& x9 ]: {' K# D$ [
* T& U& h& M' N0 V, d
. v. P3 b! h- R" T4 ~% b# E8 ^ARDUINO接口:SCL及SDA
" B. I' |+ d3 m; p) V. `0 {* x$ N8 u" X/ p d
6 O8 U1 g* V% c) v
# i/ j# H) V, x/ i) k$ cNUCLEO-L476RG上对应的接口$ ]% L4 |1 E7 T6 P5 _
9 s, G% j" j3 D; [
$ [% }* H2 i, z1 U* ]/ i3 I
6 R- p) [7 V, U* Q) c
; s. p) \+ k) G, s
写程序之前,要了解硬件的基本特性,首先是STM32L476RG上I2C接口支持的特性;其次是AT24C02的特性。, l: d, Q- Z7 i) ]
7 V. ^2 C; c* ~. E, [" U4 kSTM32L476RG的I2C功能框图如下/ _! n3 V& u |/ a5 f6 z
E" l) E5 G; g' ?
( p. r, K- v/ S
7 V# |& {3 l% _2 a, R% }; c8 Z) o值得一提的是,L4的I2C有自己独立的内核时钟,可以在SYSCLK、PCLK及HSI中任选一种做为I2C的内核时钟。另外L4的I2C支持三种不同的通信速率:100KHZ, 400KHZ,1MHZ。由于AT24C02只支持最高400KHZ,所以这里就选择400KHZ做为内核时钟。( R P% d- ~7 B2 P1 R! Z: j
+ [# y! `3 d- x! C6 }创建I2C测试工程,建立过程略。几点要注意的地方。
. E' y8 J% L, r4 e% D, c* F7 {2 |& G, Z0 u! Z' f
首先,本例程中使用的是I2C1,对应的SCL及SDA引脚为PB8及PB9,时钟配置后SYSCLK为80MHZ。
" U. S% U' _9 u' R2 c$ Q( |$ C) v/ ^3 q; K+ ^3 f, w, u8 K
I2C硬件配置参数如图" p+ p/ G- J% M3 t8 ]# f- ?
, ~0 @3 L0 g, P4 |2 A, p$ H* V }
S* n+ U! ]; y5 o9 @0 A# }& u8 f4 T& P. }8 D+ w) \
速度有三种可选:标准,快速,快速加。另外针对不同的特定应用,可以选择设置上升和下降沿的时间,以纳秒为单位,范围为0-120纳秒之间。注意这个TIMING参数,是灰色的,CUBEMX自动计算并设置该值,不像以前的F0和F3系列,需要一个专门的工具来设置TIMING的值,够麻烦的。- C( ?+ E( r6 _$ F5 Z4 z
! |8 v' N' _7 ]" K, `
下面是使用HAL库与EEPROM通信的主要代码。
7 T0 x j+ `+ U8 ~: o* Y2 g1 j4 g% ?
% R$ J) l' F4 b+ i; \5 @ Q1 K% q: C- /* USER CODE BEGIN PV */
# P0 ~( E1 n, Y6 T/ { - /* Private variables ---------------------------------------------------------*/! \1 Q6 ~6 r. P8 P A T& V
- #define EEPROM_ADDRESS 0xA0- ]* y, H, F" }+ ?) [" E8 H6 C
- #define EEPROM_PAGESIZE 0x11
! A, S; i5 c; ?) P, ~1 j - #define EEPROM_TIMEOUT 1000
' L: w8 O, y) c" N, _) p2 X8 w' e; v) j
& s' p6 Z- W7 e; W3 |- extern I2C_HandleTypeDef hi2c1;6 A) }5 e( [# \6 f7 o+ V+ C3 j
- 1 m- v0 b' d$ y* V; J/ B4 A
- char msg[] = "This is a test!\r\n";
" _, G6 A+ J2 K7 k+ c: v( a - * Y; j$ W2 B, i- ~3 C9 @+ T6 T
- char buf[40];
1 B; |" |' g; ` - . V0 C% v: O3 ]; h
- int16_t Remaining_Bytes;
4 P: R8 Q1 D9 a5 G& B3 {+ j0 x - uint16_t Memory_Address;6 U& k( W- o% h( ^
' R P: \& l! R# A- uint16_t len;: {9 m5 z; m: `& q4 I2 P
* \5 U0 ^! b' k- ......
) C, ]0 V, I+ l3 H! k( D- U
: l5 X4 V/ u3 m+ m: v- //write msg to eeprom
" | M/ `) ~! E6 ^8 f - Remaining_Bytes = strlen(msg);
" |! L9 _* ]1 h - Memory_Address = 0;
6 k7 R' ~5 w/ f -
5 D, y* x) j- O: h1 x - while(Remaining_Bytes > 0)
5 S" U% G( g h1 M - {
9 ]! u6 t7 ?+ U - if(HAL_I2C_Mem_Write_DMA(&hi2c1, EEPROM_ADDRESS, Memory_Address, I2C_MEMADD_SIZE_8BIT, (uint8_t *)(msg + Memory_Address), EEPROM_PAGESIZE) != HAL_OK)- W# s! P6 k$ s
- {' ?+ b3 r3 J; R0 z3 t8 a. k
- Error_Handler();+ X- [$ r; o' }5 i8 S; X7 U8 L
- 6 K9 Y# A$ u' v/ w6 U6 m
- }
: w) x* X+ B( I _ -
3 J: C: I, Z$ z" D* s' Y4 W - while(HAL_I2C_GetState(&hi2c1) != HAL_I2C_STATE_READY); Z+ G2 g' W" ?+ ^( ~8 Z
- {9 ]" c; J! B% w% o
- }" n. k; V6 ~% X
-
* H z; H4 t, ~7 v - while (HAL_I2C_IsDeviceReady(&hi2c1, EEPROM_ADDRESS, 10, 300) == HAL_TIMEOUT);
. C s3 n/ U; f' n; }# q J - " Y. W4 |8 @% i% ?
- while (HAL_I2C_GetState(&hi2c1) != HAL_I2C_STATE_READY)
7 q! C4 n1 J5 l2 m7 C. ~0 W M0 ` - {
) z" X! }) Z: F v3 b$ X, v) j - }
1 s, ?: u2 ]7 L - ; S% ]" A2 H9 q, C2 R
- Remaining_Bytes -= EEPROM_PAGESIZE;
: Y" v3 o& x. z& s% M( b -
) L6 \3 a' V ^6 e, V4 x - Memory_Address += EEPROM_PAGESIZE;
4 x1 a. N1 X% p+ f: L4 o# l, a. J - }2 Y& s g# i7 \) ~3 i- X
- ]3 p& `" p. u i1 \
- //read msg to buff Z5 @7 S$ v1 Y' W
- 6 {& r4 K4 D ]# w% Q8 G
- if(HAL_I2C_Mem_Read_DMA(&hi2c1 , EEPROM_ADDRESS, 0, I2C_MEMADD_SIZE_8BIT, (uint8_t*)buf, strlen(msg))!= HAL_OK)4 p; k- H k p* S9 I Z. o6 U9 d; [& b
- {6 I# q2 i, Q2 O# H
- /* Reading process Error */4 @0 Q7 R) T4 H! P
- Error_Handler();
* z( \) F4 R% U - }- p. a9 V# ~7 A* Z8 v) g
-
2 v+ C/ v2 O! a+ Y: d- d - /* Wait for the end of the transfer */
8 `5 l# `" f6 R9 W, L6 l - while (HAL_I2C_GetState(&hi2c1) != HAL_I2C_STATE_READY)
+ U! f1 j- |( h- v5 `/ w7 e7 I - {- M9 s1 {' g5 N' ~9 t, I2 Z
- }
2 T8 f8 e' [/ p$ x$ n1 S8 g -
复制代码
4 c6 t+ K1 Z! c根据STD库中用户的反馈,I2C出错的原因主要由几方面造成,一是ST设计的I2C接口,通信过程中一些状态变量及标志位的设置非常严格,如果程序逻辑考虑不周全,会导致I2C停止工作,需要软复位后才能继续;另一方面,I2C接口在处理单个,双字节及多字节传输过程中要分开考虑,进一步将问题复杂化了;还有就是结合具体的I2C设备,不同的时序要求,进一步复杂化了通信过程。9 a8 z. T8 Q; W2 o& ^+ i7 z
0 i3 o5 h+ H- Z4 ]. d8 d! u这些问题在HAL库中都得到了很好的解决,为此,HAL库中设计了几种不同类型的API函数供调用。一类是通用的接收和发送函数,这类API函数将I2C接口视为类似于“流对象”,通信时从流对象中接收或发送数据;另一类则是类似于EEPROM之类的特殊存储对象,从这些存储对象中接收或发送数据时,都需要提供一个地址,以便准确定位。使用EEPROM来实验时,我们使用的是第二类API函数。两个主要的API函数原型及参数如下。
5 c$ B8 c& ?; X- j O2 H
* m; R5 v, x) w* i/ [2 X8 c- /**; x8 E |' L: Q" g; Y$ G9 W" o9 V
- * @brief Transmit in master mode an amount of data in non-blocking mode with DMA* T9 \' H3 Q3 v8 `: j
- * @param hi2c : Pointer to a I2C_HandleTypeDef structure that contains
3 B* j) I0 V8 _2 }' i: V - * the configuration information for the specified I2C.
% H9 ~ P; C+ F% _ - * @param DevAddress: Target device address
' i- {2 n. v: D! a% W - * @param pData: Pointer to data buffer
$ v' u# T' _2 w$ |# ?( W - * @param Size: Amount of data to be sent3 f+ x( u2 r9 x6 s& P% H
- * @retval HAL status; L- v) V: ?' a) o9 m6 Y* f+ ?
- */8 P/ U; v/ g- \7 c' P1 {
- HAL_StatusTypeDef HAL_I2C_Master_Transmit_DMA(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size)
复制代码- /**' [% m8 b: ~5 {2 f+ H
- * @brief Reads an amount of data in non-blocking mode with DMA from a specific memory address.
3 f4 L1 O* @/ \ r) l( ` - * @param hi2c : Pointer to a I2C_HandleTypeDef structure that contains& ^$ H+ h7 x! H5 d! T
- * the configuration information for the specified I2C.
3 P+ u$ J: N$ w; J; { - * @param DevAddress: Target device address
8 y S9 m3 ?0 z: R# {) |7 f - * @param MemAddress: Internal memory address
8 p: p: y- i4 O( Y - * @param MemAddSize: Size of internal memory address) z, O' q2 L1 i# H3 g& C
- * @param pData: Pointer to data buffer; ^& ]/ X3 v2 ]. N' O* O
- * @param Size: Amount of data to be read+ x- @0 b( p6 h, X! v# k
- * @retval HAL status
: z, @* i: x' D( A6 Z6 m# o - */0 I. K$ }. d( y! l$ u$ |1 k
- 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)
复制代码 * }% L% `1 ]- S8 _, ^4 I/ n
关于这两个函数的更详细的调用方法及说明,请参考API文档。0 P! s* @" ^5 i. [* R4 ^. q5 V
* [# h; Y' B' A* {4 z注意在具体操作EEPROM的时候,还要注意,EEPROM分字节读写及页面读写两种类型,对HAL API的调用还要进行适当的修改。+ N4 k) W! c$ K V& d
0 A: u5 a# T2 ^8 v9 F
由于ST HAL API做了大量的后台工作,上面的代码看起来过于简单了。但实际上API函数在后台做了大量的工作,这不正是我们期待的么!
& B7 n: x8 Z7 v9 P: x V- N
( i+ b+ r7 ] \+ T最后来看看运行结果。& F |) W. Q% E* z# X V% n! ^4 r
# Y( \ o# E- _! T8 q
UART打印的写入及读取取结果- P1 T- K' `; @: q
* u% s; a4 u' l4 A& G& ~2 S
# S q( u- T# M8 f0 m
/ f5 @/ Q3 Y/ L5 m' ~2 u$ V, I3 s逻辑分析仪LA: \. I/ `: s) w
8 x0 u4 D# O# L
9 N( b1 f9 l% N
, ]4 o" b# i w( x
I2C通信全程,LA分析结果. v1 \$ [3 g2 m: }
% b! Y1 b! Q) S
9 F. v( y% S+ `9 V( w0 B/ M
0 e; u1 T% b; d) f" \: ?( X) G# H开始通信,顺序依次是开始信号,地址,EEPROM写入地址,第一个数据字节
m1 M0 J( u( q% Z) J& }/ x' H' K& D I, V
4 w8 K+ @0 u) ?6 A) c! g
2 S- {7 i# ~) n. f4 t
字符0x0D, 0x0A, 结束信号。
3 P, U( ?# X7 t2 n. k/ t+ s
6 l2 J& {* E: z& y( }' X) e
) S* k, f5 F' i9 w9 W
3 D; ?6 }+ @( |# v# W5 p
接收开始,顺序依次是开始信号,地址,EEPROM读取地址,第一个数据字节
# A" J* \9 F0 n* _7 g# u9 B/ F. F
* s$ L2 s6 k( m
6 Z/ B$ v U. @( l
; {) \# s- e1 S* W9 R
读取结束
# a$ u$ C h2 S& x' X3 y2 e9 q M) z* y3 }' o# c2 {
5 w) p( ^# p2 X$ C
; J! A+ r' c5 Q l" k; Z
/ q. i! }) L% h% i$ X4 |/ S% t) N& v# @3 b" k7 O9 B7 _
附工程! P# z0 a* _; H+ @0 F
* x- h# E L# c. g; P# |) b
eeprom.zip
(3.17 MB, 下载次数: 201)
|
{
Error_Handler();
};程序直接进入Error_Handler()函数,请问是什么原因呢?我检查了很多地方,还是不行,求指教,非常感谢!(测试环境stm32l476rg+at24c02,后来用了at24c16测试还是失败)
1、移植,直接秒杀不用多说
2、效率,IIC的硬件通讯过程中用了太多的访问标志位,实际效率不会增加,一样是让CPU在耗,
所以,效率反而没有模拟IIC高,因为GPIO的反转速率轻松满足IIC的通信速率,所以
模拟IIC照样更胜1筹。9 ?) H' K1 e T5 s# f/ {& r
3、复杂性,ST的通病,反而模拟IIC更加把协议变得很直观。
纵上所述,感觉这个硬件IIC就是个鸡肋。
梦源的牌子吧,100M