Arduino UNO의 I2C SCL 주파수를 극단적으로 낮추고 싶을 때

10983 단어 Arduino
참고 : htps : // 에 l 치카. 이 m/아 rc cぇ/바에 bfc34-dc91-4146-82 예-50 아 5714 아 f578/

Arduino UNO (ATmega328P)의 I2C 주변 장치를 Wire 라이브러리로 구동하는 경우 SCL 주파수는 Wire.setClock() 함수로 설정합니다. 설정하지 않을 경우의 디폴트값은 100 kHz이다.

Arduino UNO(ATmega328P)의 SCL 주파수 SCLfreq는 데이터시트에 의하면 TWBR[7:0] , TWSR[1:0]
SCLfreq = F_CPU/(16 + 2 * TWBR * 4^TWPS)
( TWPS 는 0~255, TWBR 는 0~3)

따라서 SCL 주파수는 (F_CPU/16) ~ (F_CPU/32656) 범위에서 설정할 수 있습니다. 그러나 TWPS 함수는 Wire.setClock()TWSR[1:0] 에 고정되어 있으므로 (F_CPU/16) ~ (F_CPU/526)의 범위에서만 설정할 수 있다 (F_CPU = 16 MHz로 하면 SCLfreq = 1 약 30 kHz).

분주비를 526보다 높게 하고 싶은 경우는 0b00 함수는 사용하지 않고 Wire.setClock()

Arduino UNO (ATmega328P)의 I2C SCL 주파수를 극단적으로 낮추고 싶을 때의 샘플



예를 들어 아래와 같이 원하는 SCL 주파수에서 TWBR[7:0] 값(= TWSR[1:0] )과 TWPS 값을 구하여 설정한다. 이것으로 32656 분주(F_CPU = 16 MHz로 하면 SCLfreq = 약 490 Hz)까지 낮출 수 있다. 할 수 있다는 것만으로 그다지 의미는 없다.

확인을 위한 샘플 파일(.ino)



set_SCL_freq.h를 포함하고 TWSR[1:0] 함수로 SCL 주파수를 설정합니다. 여기에서는 500 Hz로 설정해 본다.
#include <Wire.h>
#include "set_SCL_freq.h" // これをインクルードする。

void setup() {
  Wire.begin();
  //Wire.setClock(100000UL);
  set_SCL_Clock(16000000UL, 500); // 引数は(F_CPU, 所望のSCL周波数)
}

void loop() {
  Wire.beginTransmission(8);
  Wire.endTransmission();
  delay(500);
}

헤더 파일



set_SCL_freq.h
#ifndef SET_SCL_FREQ_H
#define SET_SCL_FREQ_H

//#include <avr/io.h>

#define MAX(a, b)                (((a) > (b)) ? (a) : (b))
#define MIN(a, b)                (((a) < (b)) ? (a) : (b))
#define CONSTRAIN(val, min, max) (MIN((MAX((val), (min))), (max)))
#define SQUARE(a)                ((a) * (a))

// TWSR[1:0]、TWBR[7:0]の各値から、F_CPUが何分周されるのかを求めるマクロ。
#define DIV_REG(twps, twbr)  (((twbr) * (1 << ((twps) * 2)) + 8) * 2)

const uint8_t TWPS_MAX = 3;
const uint8_t TWBR_MAX = 255;

const uint16_t DIV_MIN = DIV_REG(0, 0);               // 最小16分周まで可
const uint16_t DIV_MAX = DIV_REG(TWPS_MAX, TWBR_MAX); // 最大32656分周まで可

const uint16_t DIV_MAX0 = DIV_REG(0, TWBR_MAX); //  526。TWPS = 0のときの最大分周比。
const uint16_t DIV_MAX1 = DIV_REG(1, TWBR_MAX); // 2056。TWPS = 1のときの最大分周比。
const uint16_t DIV_MAX2 = DIV_REG(2, TWBR_MAX); // 8176。TWPS = 2のときの最大分周比。

// F_CPUと所望のSCL周波数との比を求める函数。
uint16_t calc_div(uint32_t f_cpu, uint32_t scl_freq){
  uint32_t div = f_cpu / scl_freq;
  return (uint16_t)(CONSTRAIN(div, DIV_MIN, DIV_MAX));
}

// TWSR[1:0] (=TWPS)の値を求める函数。
uint8_t calc_twps(uint16_t div){
  uint8_t twps;
  if     (div <= DIV_MAX0){twps = 0;} // F_CPUとSCL周波数との比が     ~  526の場合
  else if(div <= DIV_MAX1){twps = 1;} // F_CPUとSCL周波数との比が 528 ~ 2056の場合
  else if(div <= DIV_MAX2){twps = 2;} // F_CPUとSCL周波数との比が2064 ~ 8176の場合
  else                    {twps = 3;} // F_CPUとSCL周波数との比が8208 ~     の場合
  return twps;
}

// TWBR[7:0]の値を求める函数。
uint8_t calc_twbr(uint32_t f_cpu, uint32_t scl_freq, uint8_t twps){
  scl_freq = CONSTRAIN(scl_freq, f_cpu / DIV_MAX, f_cpu / DIV_MIN);
  return (uint8_t)((f_cpu - (scl_freq * 16)) / ((scl_freq * SQUARE(1 << twps)) * 2));
}

// TWSR[1:0] (=TWPS)、TWBR[7:0]を書き換えるための函数。
void set_SCL_Clock(uint32_t f_cpu, uint32_t scl_freq){
  uint16_t div = calc_div(f_cpu, scl_freq); // 所望の分周比を求めて、
  uint8_t twps = calc_twps(div);            // TWSR[1:0] (=TWPS)の値を求めて、
  TWSR = (TWSR & ~0b11) | twps;             // その値でTWSR[1:0]を書き換えて、
  TWBR = calc_twbr(f_cpu, scl_freq, twps);  // TWBRレジスタも書き換える。
}

#endif

실행 결과



SCL을 관측하고 있다.

좋은 웹페이지 즐겨찾기