Compare commits

...

2 Commits

Author SHA1 Message Date
Jack Humbert 5db4d16300 Merge branch 'master' of github.com:qmk/qmk_firmware into twi2c 7 years ago
Jack Humbert f0a425c537 start implementation 7 years ago

@ -0,0 +1,228 @@
#include <avr/io.h>
#include <util/twi.h>
#include <avr/interrupt.h>
#include <stdbool.h>
#include <stdlib.h>
#include "twi.h"
#ifndef F_SCL
#define F_SCL 100000UL // SCL frequency
#endif
#ifndef PRESCALER
#define PRESCALER 1
#endif
// Limits the amount of we wait for any one i2c transaction.
// Since were running SCL line 100kHz (=> 10μs/bit), and each transactions is
// 9 bits, a single transaction will take around 90μs to complete.
//
// (F_CPU/SCL_CLOCK) => # of μC cycles to transfer a bit
// poll loop takes at least 8 clock cycles to execute
#ifdef TWI_TIMEOUT
#ifndef TWI_TX_SIZE
#define TWI_TX_SIZE 9
#endif
#endif
// Wait for an twi operation to finish
inline static
void twi_wait(uint8_t status) {
#ifdef TWI_TIMEOUT
uint16_t lim = 0;
while ( !(TWCR & (_BV(status))) && lim < (TWI_TX_SIZE+1)*(F_CPU/F_SCL)/8)
lim++;
#else
while ( !(TWCR & (_BV(status))) );
#endif
}
void twi_master_init(void) {
TWBR = (uint8_t) ((((F_CPU / F_SCL) / PRESCALER) - 16 ) / 2);
}
void twi_slave_init(uint8_t address) {
TWAR = address << 0; // slave i2c address
TWCR = _BV(TWIE) | _BV(TWEA) | _BV(TWINT) | _BV(TWEN);
}
uint8_t twi_master_start(uint8_t address) {
// reset TWI control register and transmit start
TWCR = 0;
TWCR = _BV(TWINT) | _BV(TWEN) | _BV(TWSTA);
twi_wait(TWINT);
// check if the start condition was successfully transmitted
if (TW_STATUS != TW_START)
return 1;
// set slave address and transmit
TWDR = address;
TWCR = _BV(TWINT) | _BV(TWEN);
twi_wait(TWINT);
// check if the device has acknowledged the READ / WRITE mode
if ( (TW_STATUS != TW_MT_SLA_ACK) && (TW_STATUS != TW_MR_SLA_ACK) )
return 1;
return 0;
}
void twi_master_stop(void) {
// transmit STOP condition
TWCR = _BV(TWINT) | _BV(TWEN) | _BV(TWSTO);
twi_wait(TWSTO);
}
uint8_t twi_write(uint8_t data) {
// load data into data register and start transmission of data
TWDR = data;
TWCR = _BV(TWINT) | _BV(TWEN);
twi_wait(TWINT);
if ( TW_STATUS != TW_MT_DATA_ACK )
return 1;
return 0;
}
uint8_t twi_read(bool ack) {
// start TWI module and acknowledge data after reception
if (ack)
TWCR = _BV(TWINT) | _BV(TWEN) | _BV(TWEA);
else
TWCR = _BV(TWINT) | _BV(TWEN);
twi_wait(TWINT);
return TWDR;
}
uint8_t twi_transmit(uint8_t address, uint8_t* data, uint16_t length) {
if (twi_start(address | TW_WRITE))
return 1;
for (uint16_t i = 0; i < length; i++) {
if (twi_write(data[i]))
return 1;
}
twi_stop();
return 0;
}
uint8_t twi_receive(uint8_t address, uint8_t* data, uint16_t length) {
if (twi_start(address | TW_READ))
return 1;
for (uint16_t i = 0; i < (length-1); i++) {
data[i] = twi_read(true);
}
data[(length-1)] = twi_read(false);
twi_stop();
return 0;
}
uint8_t twi_writeReg(uint8_t devaddr, uint8_t regaddr, uint8_t* data, uint16_t length) {
if (twi_start(devaddr | TW_WRITE))
return 1;
twi_write(regaddr);
for (uint16_t i = 0; i < length; i++) {
if (twi_write(data[i]))
return 1;
}
twi_stop();
return 0;
}
uint8_t twi_readReg(uint8_t devaddr, uint8_t regaddr, uint8_t* data, uint16_t length)
{
if (twi_start(devaddr))
return 1;
twi_write(regaddr);
if (twi_start(devaddr | TW_READ))
return 1;
for (uint16_t i = 0; i < (length-1); i++) {
data[i] = twi_read(true);
}
data[(length-1)] = twi_read(false);
twi_stop();
return 0;
}
uint8_t buffer_address = 0;
ISR(TWI_vect) {
uint8_t data;
switch (TW_STATUS) {
case TW_SR_SLA_ACK:
buffer_address = 0xff;
TWCR |= _BV(TWIE) | _BV(TWINT) | _BV(TWEA) | _BV(TWEN);
break;
case TW_SR_DATA_ACK:
data = TWDR;
if (buffer_address == 0xff) {
// store address to read from later
buffer_address = data;
TWCR |= _BV(TWIE) | _BV(TWINT) | _BV(TWEA) | _BV(TWEN);
} else {
// store data from address and increment
rxbuffer[buffer_address] = data;
buffer_address++;
if (buffer_address < 0xFF) {
// ack
TWCR |= _BV(TWIE) | _BV(TWINT) | _BV(TWEA) | _BV(TWEN);
} else {
// nack
TWCR &= ~_BV(TWEA);
TWCR |= _BV(TWIE) | _BV(TWINT) | _BV(TWEN);
}
}
break;
case TW_ST_SLA_ACK:
case TW_ST_DATA_ACK:
data = TWDR;
if (buffer_address == 0xFF) {
buffer_address = data;
}
TWDR = txbuffer[buffer_address];
buffer_address++;
if (buffer_address < 0xFF) {
// ack
TWCR |= _BV(TWIE) | _BV(TWINT) | _BV(TWEA) | _BV(TWEN);
} else {
// nack
TWCR &= ~_BV(TWEA);
TWCR |= _BV(TWIE) | _BV(TWINT) | _BV(TWEN);
}
break;
case TW_BUS_ERROR:
TWCR = 0;
break;
default:
TWCR |= _BV(TWIE) | _BV(TWEA) | _BV(TWEN);
break;
}
}

@ -0,0 +1,4 @@
#ifndef TWI_H
#define TWI_H
#endif
Loading…
Cancel
Save