mikrokontrollersystemer-pro.../prosjekt.X/i2c.c

126 lines
3.4 KiB
C
Raw Normal View History

#include "i2c.h"
#include "command-handler.h"
// Basic I2C handling structure is heavily inspired by:
// https://github.com/microchip-pic-avr-examples/avr128db48-bare-metal-twi-mplab/blob/master/twi-client.X/peripherals/TWI/TWI_client.c
// We need to keep track of if the stop confition is in regards to a write or
// read request
volatile bool last_action_write = false;
// Buffer to hold the received data
volatile uint8_t i2c_recv_buf[I2C_RECV_BUF_SIZE] = {0};
// Counter to know which datapoint we're on
volatile uint8_t i2c_recv_len = 0;
void init_i2c(void) {
// Pin setup
PORTA.DIRSET = PIN2_bm | PIN3_bm;
PORTA.PINCTRLUPD = PIN2_bm | PIN3_bm;
// Enable operating in debug
TWI0.DBGCTRL = TWI_DBGRUN_bm;
// Initialize the control A register
TWI0.CTRLA = TWI_INPUTLVL_I2C_gc // I2C voltage transition level
| TWI_SDASETUP_4CYC_gc // Four clock cycles setup time
| TWI_SDAHOLD_50NS_gc // 50ns SDA hold time
| TWI_FMPEN_OFF_gc // Standard SPI timing
;
// The device's slave address
TWI0.SADDR = 0x42;
// Enable acting as a slave
TWI0.SCTRLA = TWI_DIEN_bm // Enable data interrupt
| TWI_APIEN_bm // Enable more interrupts
| TWI_PIEN_bm // Enable stop flag interrupt
| PIN2_bm // Respond to all TWI addresses
| TWI_ENABLE_bm // Enable acting as a slave
;
// Reset the received stuff
i2c_reset_recv();
}
// Reset received counter and clear receive buffer
void i2c_reset_recv() {
i2c_recv_len = 0;
for (int i = 0; i < I2C_RECV_BUF_SIZE; i++) {
i2c_recv_buf[i] = 0;
}
}
void i2c_write_handler(uint8_t data) {
last_action_write = true;
// Validate that we are not overflowing the buffer
if (i2c_recv_len >= I2C_RECV_BUF_SIZE) { return; }
// Write the data to the receive buffer
i2c_recv_buf[i2c_recv_len] = data;
i2c_recv_len++;
}
void i2c_read_handler() {
last_action_write = false;
TWI0.SDATA = route_command(i2c_recv_len);
i2c_recv_len++; // Increment the counter of the current amount of requests
}
void i2c_stop_handler() {
if (last_action_write) {
// Parse the received command data
parse_command(i2c_recv_buf, i2c_recv_len);
2024-04-24 13:43:27 +00:00
// If the received command is a write only command we want to route it now.
if (i2c_recv_buf[0] == CLEAR_BULK_FAN_SPEED || i2c_recv_buf[0] == WRITE_CONFIG) {
2024-04-24 13:43:27 +00:00
route_command(0);
}
}
2024-04-24 13:43:27 +00:00
// Reset the buffer for future transmissions
i2c_reset_recv();
}
// Address received
ISR(TWI0_TWIS_vect) {
// Disable interrupts while handling I2C
cli();
// Check for the data interrupt flag
if (TWI0.SSTATUS & TWI_DIF_bm) {
if (((TWI0.SSTATUS & TWI_DIR_bm) >> TWI_DIR_bp) == 0) {
// Data write Controller -> Target
uint8_t data = TWI0.SDATA;
// Send the data to the write handler
i2c_write_handler(data);
} else {
// Data read Controller <- Target
i2c_read_handler();
}
// Acknowledge having received
TWI0.SCTRLB = TWI_ACKACT_ACK_gc | TWI_SCMD_RESPONSE_gc;
}
// Check for address match or STOP
if (TWI0.SSTATUS & TWI_APIF_bm) {
if (TWI0.SSTATUS & TWI_AP_ADR_gc) {
// Address match, just send ack
TWI0.SCTRLB = TWI_ACKACT_ACK_gc | TWI_SCMD_RESPONSE_gc;
} else {
// STOP condition received
i2c_stop_handler();
// Send ACK
TWI0.SCTRLB = TWI_ACKACT_ACK_gc | TWI_SCMD_RESPONSE_gc;
}
}
// Re-enable interrupts
sei();
}