Last active
September 2, 2021 12:18
-
-
Save malja/1073591f4c84dc9b83e26f96e7881f50 to your computer and use it in GitHub Desktop.
MSP430FR50431 I2C master with interruptions
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
#include <i2c_master.h> | |
// BIT_SET is macro for REGISTER |= (BIT) | |
// BIT_CLEAR is macro for REGISTER &= ~(BIT) | |
// | |
#include <macros.h> | |
#include <driverlib.h> | |
#include <config.h> | |
#include <string.h> | |
#include <clock.h> | |
// Current bus status | |
typedef enum i2c_bus_status { | |
// Ready for write/read operation | |
I2C_BUS_STATUS_IDLE, | |
// Working on reading/writing operation | |
I2C_BUS_STATUS_BUSY, | |
// Last operation ended with error | |
I2C_BUS_STATUS_ERROR, | |
} i2c_bus_status; | |
#if CONFIG_I2C_BUFFER_LENGTH >= 15 | |
#warning "CONFIG_I2C_BUFFER_LENGTH is set to value larger than 15 bytes. Please change index and length ragne in bus structure." | |
#endif | |
// Configuration for I2C bus | |
typedef struct i2c_bus { | |
struct { | |
// Buffer for writing is pre-allocated | |
uint8_t tx_buffer[CONFIG_I2C_BUFFER_LENGTH]; | |
// Buffer for reading is provided by the user | |
uint8_t *rx_buffer; | |
// Index in tx/rx_buffer | |
uint8_t index:4; | |
// Used length of tx/rx_buffer | |
uint8_t length:4; | |
// Should write operation end with STOP? | |
// Note: read operation always end with STOP | |
bool end_with_stop; | |
} data; | |
// Current bus status | |
i2c_bus_status status; | |
} i2c_bus; | |
typedef enum i2c_mode { | |
I2C_MODE_READ, | |
I2C_MODE_WRITE | |
} i2c_mode; | |
/////////////////////////////////////////////////////////////////////////////// | |
// POMOCNÉ FUNKCE | |
/////////////////////////////////////////////////////////////////////////////// | |
static i2c_bus bus; | |
static void set_slave_address(uint8_t address) { | |
// Save slave address | |
UCB1I2CSA = address; | |
// Using 7-bit address | |
BIT_CLEAR(UCB1CTLW0, UCSLA10); | |
} | |
static void set_mode(i2c_mode mode) { | |
if (mode == I2C_MODE_WRITE) { | |
// Write mode | |
BIT_SET(UCB1CTLW0, UCTR); | |
// Turn off RX interrupt | |
BIT_CLEAR(UCB1IE, UCRXIE0); | |
// Turn on TX interrupt | |
BIT_SET(UCB1IE, UCTXIE0); | |
} else { | |
// Read mode | |
BIT_CLEAR(UCB1CTLW0, UCTR); | |
// Turn off TX interrupt | |
BIT_CLEAR(UCB1IE, UCTXIE0); | |
// Turn on RX interrupt | |
BIT_SET(UCB1IE, UCRXIE0); | |
} | |
} | |
static void send_start(uint8_t address, i2c_mode mode) { | |
set_slave_address(address); | |
set_mode(mode); | |
bus.status = I2C_BUS_STATUS_BUSY; | |
// Send START | |
BIT_SET(UCB1CTLW0, UCTXSTT); | |
// If only one byte is read, STOP must be sent before | |
// the last byte. | |
// https://www.ti.com/lit/an/slaa734a/slaa734a.pdf | |
if (bus.data.length == 1 && mode == I2C_MODE_READ) { | |
BIT_SET(UCB1CTLW0, UCTXSTP); | |
} | |
} | |
// Wait for the last operation to finish | |
static error wait_to_finish() { | |
uint32_t ms_start = millis(); | |
while(bus.status == I2C_BUS_STATUS_BUSY) { | |
if (millis() - ms_start >= 10) { | |
return ERROR_I2C_TIMEOUT; | |
} | |
} | |
return bus.status == I2C_BUS_STATUS_ERROR ? ERROR_FAIL : ERROR_NONE; | |
} | |
/////////////////////////////////////////////////////////////////////////////// | |
// VEŘEJNÉ FUNKCE | |
/////////////////////////////////////////////////////////////////////////////// | |
void i2c_master_init() { | |
// Reset USCI, so I can set it up | |
BIT_SET(UCB1CTLW0, UCSWRST); | |
// Nastavení na I2C | |
// - UCSYNC = synchronous communication, probably not needed for | |
// eUSCI_B, since it is always set to 1 | |
// - UCMST = master | |
// - UCMODE_3 = I2C protocol | |
// - UCSSEL_2 = clock source is set to SMCLK | |
// - UCWRST = keep reset mode | |
UCB1CTLW0 = UCSYNC | UCMST | UCMODE_3 | UCSSEL_2 | UCSWRST; | |
// Set prescaler (= CONFIG_I2C_PRESCALER equals to 10, since | |
// SMCLK = 1MHz and I want 100 kHz) | |
UCB1BRW = CONFIG_I2C_PRESCALER; | |
// Leave reset mode, eUSCI now works | |
// Note: IE and IFG is cleared automatically | |
BIT_CLEAR(UCB1CTLW0, UCSWRST); | |
} | |
error i2c_master_write(uint8_t device_address, uint8_t *data, uint8_t data_length, bool send_stop) { | |
ARG_NOT_NULL(data); | |
if (data_length > CONFIG_I2C_BUFFER_LENGTH) { | |
return ERROR_NO_MEMORY; | |
} | |
// Prepare buffer | |
memcpy(bus.data.tx_buffer, data, data_length); | |
bus.data.index = 0; | |
bus.data.length = data_length; | |
bus.data.end_with_stop = send_stop; | |
send_start(device_address, I2C_MODE_WRITE); | |
return wait_to_finish(); | |
} | |
error i2c_master_read(uint8_t device_address, uint8_t *data, uint8_t data_length) { | |
ARG_NOT_NULL(data); | |
if (data_length > CONFIG_I2C_BUFFER_LENGTH) { | |
return ERROR_NO_MEMORY; | |
} | |
// Připraví buffer | |
bus.data.rx_buffer = data; | |
bus.data.index = 0; | |
bus.data.length = data_length; | |
send_start(device_address, I2C_MODE_READ); | |
return wait_to_finish(); | |
} | |
#pragma vector = EUSCI_B1_VECTOR | |
__interrupt void i2c_isr() { | |
switch(UCB1IV) { | |
// TX buffer is empty | |
case UCIV__UCTXIFG0: | |
// Still have something to send | |
if (bus.data.index < bus.data.length) { | |
// Set next byte | |
UCB1TXBUF = bus.data.tx_buffer[bus.data.index++]; | |
// No data to send | |
} else { | |
// Should the STOP be sent? | |
if (bus.data.end_with_stop) { | |
BIT_SET(UCB1CTLW0, UCTXSTP); | |
} | |
// Operation ended | |
bus.status = I2C_BUS_STATUS_IDLE; | |
} | |
break; | |
// RX buffer is full | |
case UCIV__UCRXIFG0: | |
// Reading only one byte is handled by send_start function. | |
// Now handle the case, that the next byte would be the last. | |
// We need to send STOP with the (N-1)th byte | |
if (bus.data.length > 1 && bus.data.index + 1 == bus.data.length) { | |
BIT_SET(UCB1CTLW0, UCTXSTP); | |
bus.status = I2C_STATUS_IDLE; | |
} | |
// Read byte (if expected) | |
if (bus.data.index < bus.data.length) { | |
bus.data.rx_buffer[bus.data.index++] = UCB1RXBUF; | |
} else if (bus.data.index == bus.data.length) { | |
bus.status = I2C_BUS_STATUS_IDLE; | |
// STOP je odeslán nahoře :) | |
} else { | |
// Na busu se vyskytla chyba! | |
bus.status = I2C_BUS_STATUS_ERROR; | |
} | |
break; | |
// SCL je držen low příliš dlouho | |
case UCIV__UCCLTOIFG: | |
bus.status = I2C_BUS_STATUS_ERROR; | |
break; | |
} | |
} |
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
#ifndef INCLUDE_I2C_MASTER_H_ | |
#define INCLUDE_I2C_MASTER_H_ | |
#ifdef __cplusplus | |
extern "C" | |
{ | |
#endif | |
#include <error.h> | |
#include <stdint.h> | |
#include <stdbool.h> | |
void i2c_master_init(); | |
error i2c_master_write(uint8_t device_address, uint8_t *data, uint8_t data_length, bool send_stop); | |
error i2c_master_read(uint8_t device_address, uint8_t *data, uint8_t data_length); | |
#ifdef __cplusplus | |
} | |
#endif | |
#endif /* INCLUDE_I2C_MASTER_H_ */ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment