问题
使用Arduino Mega2560板,Wire.h
库进行I2C通信时,需要调节I2C通信的速率。
setClock()方法
直接使用Wire.h
库的setClock()
方法修改I2C通信的时钟频率。
机翻如下:
新的问题
默认I2C通信的时钟频率为100kHz,计划使用10kHz频率时,直接调节上述setClock()
函数参数为10000,但用示波器测量得到的SCL频率仍约为40kHz。
解决过程
翻了翻Wire
库的代码以及Mega2560芯片的数据手册,截取了部分内容如下:
Wire.cpp
片段
void TwoWire::setClock(uint32_t clock)
{
twi_setFrequency(clock);
}
Wire
库还依赖了twi.c
文件,twi.c
片段
void twi_init(void)
{
...
// initialize twi prescaler and bit rate
cbi(TWSR, TWPS0);
cbi(TWSR, TWPS1);
TWBR = ((F_CPU / TWI_FREQ) - 16) / 2;
// 此处TWI_FREQ在twi.h文件中宏定义为1000000,即IIC默认时钟频率为100kHz
// #define TWI_FREQ 100000L
...
}
void twi_setFrequency(uint32_t frequency)
{
TWBR = ((F_CPU / frequency) - 16) / 2;
/* twi bit rate formula from atmega128 manual pg 204
SCL Frequency = CPU Clock Frequency / (16 + (2 * TWBR))
note: TWBR should be 10 or higher for master mode
It is 72 for a 16mhz Wiring board with 100kHz TWI */
}
SCL时钟频率计算公式
TWPS预分频数表
由上代码片段结合数据手册可知,在初始化时,预分频数为1,TWBR
为8位寄存器,其最大值为255,则SCL频率在此情况下最低为
16
M
H
z
16
+
2
∗
255
≈
30.4
k
H
z
\frac{16MHz}{16+2*255} \approx 30.4kHz
16+2∗25516MHz≈30.4kHz。
所以为了满足SCL时钟频率达到10kHz的目的,需要将预分频器调节为4分频,即把TWPS0
置1,同时修正TWBR
计算公式。
最终将twi.c
文件中
cbi(TWSR, TWPS0);
TWBR = ((F_CPU / TWI_FREQ) - 16) / 2;
修改为
sbi(TWSR, TWPS0);
TWBR = ((F_CPU / TWI_FREQ) - 16) / 2 / 4;
补充
//-----------位操作定义------------------------
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
可知为sbi(sfr, bit)
为将寄存器指定位置1,cbi(sfr, bit)
为将寄存器指定位置0。
参考资料
setClock() - Arduino Reference
请教:cbi()/sbi()的用法 (amobbs.com 阿莫电子论坛 - 东莞阿莫电子网站)
ATmega640-1280-1281-2560-2561-Datasheet-DS40002211A.pdf