Skip to content

Instantly share code, notes, and snippets.

@ti-nspire
Last active August 25, 2020 05:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ti-nspire/afc5a46a61a8f79853de2406c33efb26 to your computer and use it in GitHub Desktop.
Save ti-nspire/afc5a46a61a8f79853de2406c33efb26 to your computer and use it in GitHub Desktop.
I2C library for ATmega328P
/*
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;
/*
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
/*
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