Last active
August 25, 2020 05:01
-
-
Save ti-nspire/afc5a46a61a8f79853de2406c33efb26 to your computer and use it in GitHub Desktop.
I2C library for ATmega328P
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
I2C library for ATmega328P | |
*/ | |
#include "I2CClass.h" | |
#include "set_SCL_freq.h" | |
void I2CClass::waitForComplete(){ | |
loop_until_bit_is_set(TWCR, TWINT); | |
} | |
void I2CClass::init(uint32_t f_cpu, uint32_t scl_freq){ | |
TWCR |= (1 << TWEN); // 念のため真っ先にI2Cを有効化しておく。 | |
set_SCL_freq(f_cpu, scl_freq); | |
} | |
void I2CClass::quit(){ | |
TWCR &= ~(1 << TWEN); | |
} | |
void I2CClass::start(){ | |
TWCR = (1 << TWINT | 1 << TWEN | 1 << TWSTA); | |
waitForComplete(); | |
} | |
void I2CClass::stop(){ | |
TWCR = (1 << TWINT | 1 << TWEN | 1 << TWSTO); | |
} | |
uint8_t I2CClass::readByteACK(){ | |
TWCR = (1 << TWINT | 1 << TWEN | 1 << TWEA); | |
waitForComplete(); | |
return TWDR; | |
} | |
void I2CClass::readBytesACK(uint16_t n, uint8_t *buff){ | |
for(uint16_t i=0; i<n; i++){ | |
buff[i] = readByteACK(); | |
} | |
} | |
uint8_t I2CClass::readByteNACK(){ | |
TWCR = (1 << TWINT | 1 << TWEN); | |
waitForComplete(); | |
return TWDR; | |
} | |
void I2CClass::readBytesNACK(uint16_t n, uint8_t *buff){ | |
readBytesACK(n-1, buff); | |
buff[n-1] = readByteNACK(); | |
} | |
void I2CClass::writeByte(uint8_t byte){ | |
TWDR = byte; | |
TWCR = (1 << TWINT | 1 << TWEN); | |
waitForComplete(); | |
} | |
void I2CClass::writeBytes(uint16_t n, uint8_t *buff){ | |
for(uint16_t i=0; i<n; i++){ | |
writeByte(buff[i]); | |
} | |
} | |
void I2CClass::read(uint8_t slave_7_addr, uint8_t first_register, uint16_t n, uint8_t*buff){ | |
start(); | |
writeByte(slave_7_addr << 1); | |
writeByte(first_register); | |
start(); | |
writeByte((slave_7_addr << 1) | 1); | |
readBytesNACK(n, buff); | |
stop(); | |
} | |
void I2CClass::write(uint8_t slave_7_addr, uint8_t first_register, uint16_t n, uint8_t*buff){ | |
start(); | |
writeByte(slave_7_addr << 1); | |
writeByte(first_register); | |
writeBytes(n, buff); | |
stop(); | |
} | |
void I2CClass::write(uint8_t slave_7_addr, uint8_t which_register, uint8_t byte){ | |
start(); | |
writeByte(slave_7_addr << 1); | |
writeByte(which_register); | |
writeByte(byte); | |
stop(); | |
} | |
// コンストラクタ | |
I2CClass::I2CClass(){} | |
// 実装ファイルの中で先に実体化しておく。 | |
I2CClass i2c; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
I2C library for ATmega328P | |
*/ | |
#ifndef I2CClass_H | |
#define I2CClass_H | |
#ifndef F_CPU | |
#define F_CPU 8000000UL | |
#endif | |
#include <avr/io.h> | |
class I2CClass{ | |
private: | |
void waitForComplete(); // waits for a transmitting or receiving to complete. | |
uint8_t readByteACK(); // after receiving a byte, returns an ACK. | |
void readBytesACK(uint16_t n, uint8_t *buff); // after receiving bytes, returns an ACK. | |
uint8_t readByteNACK(); // after receiving a byte, returns a NACK. | |
void readBytesNACK(uint16_t n, uint8_t *buff); // after receiving bytes, returns a NACK. | |
void writeByte(uint8_t byte); // sends a byte。 | |
void writeBytes(uint16_t n, uint8_t *buff); // sends bytes。 | |
public: | |
void init(uint32_t f_cpu=F_CPU, uint32_t scl_freq=100000UL); // enables the I2C module. | |
void quit(); // disables the I2C module. | |
void start(); // generates the START condition. | |
void stop(); // generates the STOP condition. | |
// reads (from which slave, from which register, how many bytes, into which array) | |
void read(uint8_t slave_7_addr, uint8_t first_register, uint16_t n, uint8_t*buff); | |
// writes (into which slave, into which register, how many bytes, from which array) | |
void write(uint8_t slave_7_addr, uint8_t first_register, uint16_t n, uint8_t*buff); | |
// writes (into which slave, into which register, which byte) | |
void write(uint8_t slave_7_addr, uint8_t which_register, uint8_t byte); | |
// constuctor | |
I2CClass(); | |
}; | |
extern I2CClass i2c; | |
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
I2C library for ATmega328P | |
*/ | |
#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_freq(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 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment