I2C / SCL周波数を設定する
データシートによればSCL周波数はTWBR[7:0]
、TWSR[1:0]
(= TWPS
)の各レジスタ値から次式で求まる。
SCL_freq == F_CPU / (16 + 2 * TWBR * 4^TWPS)
TWBR
は0~255、TWPS
は0~3。
この式をTWBR
について解くと次のように変形できる。
TWBR == F_CPU / (2 * SCL_freq * (2^TWPS)^2) - 8 / (2^TWPS)^2
F_CPUについて解くと次のように変形できる。
F_CPU == 2 * SCL_freq * TWBR * (2^TWPS)^2 + 16 * SCL_freq
結局F_CPUを基準とする分周比DIVは下式で求まる。
DIV == 2 * (TWBR * 4^TWPS + 8)
こんな感じにすればF_CPUとSCL周波数とからTWBR[7:0]
、TWSR[1:0]
(= TWPS
)の各値が求まる。
#include <iostream> using namespace std; // TWSR[1:0] (== TWPS)、TWBR[7:0]の各値から、F_CPUを基準とする分周比を求める函数。 uint16_t calc_div_reg(uint8_t twps, uint8_t twbr){ return 2 * (twbr * (1 << (twps * 2)) + 8) ; } // F_CPUとSCL周波数とから、F_CPUを基準とする分周比を求める函数。 uint32_t calc_div(uint32_t f_cpu, uint32_t scl_freq){ return f_cpu / scl_freq; } // TWSR[1:0] (== TWPS)の値を求める函数。 uint8_t calc_twps(uint32_t div){ uint8_t twps; if (div <= calc_div_reg(0,255)){twps = 0;} // ~ 526 else if(div <= calc_div_reg(1,255)){twps = 1;} // 528 ~ 2056 else if(div <= calc_div_reg(2,255)){twps = 2;} // 2064 ~ 8176 else {twps = 3;} // 8208 ~ return twps; } // TWBR[7:0]の値を求める函数。 uint8_t calc_twbr(uint32_t f_cpu, uint32_t scl_freq, uint8_t twps){ uint32_t twbr = f_cpu / (2 * scl_freq * (1<<twps) * (1<<twps)) - 8 / ((1<<twps) * (1<<twps)); return (twbr > 255) ? 255 : (uint8_t)twbr; } // TWSR[1:0] (== TWPS)、TWBR[7:0]を書き換えるための函数。 uint8_t TWSR, TWBR; // ★★確認用。あとで消す。★★★★★★ void set_SCL_Clock(uint32_t f_cpu=16000000UL, uint32_t scl_freq=100000UL){ uint8_t twps = calc_twps(calc_div(f_cpu, scl_freq)); // TWSR[1:0] (== TWPS)の値を求めて、 TWSR &= ~0b11; // いったんTWSR[1:0]を消して、 TWSR |= twps; // 求まったTWPS値をTWSR[1:0]に書き込んで、 TWBR = calc_twbr(f_cpu, scl_freq, twps); // TWBRレジスタも書き換える。 printf("TWPS: %d\n", TWSR & 0b11); // ★★確認用。あとで消す。★★★★★★ printf("TWBR: %d\n", TWBR); // ★★確認用。あとで消す。★★★★★★ } int main() { set_SCL_Clock(16000000UL, 500); // (F_CPU, SCL周波数); return 0; }
実行結果: