add I²C master library
This commit is contained in:
parent
81152cfee2
commit
1d2be7ceb2
428
i2c_master.c
Normal file
428
i2c_master.c
Normal file
@ -0,0 +1,428 @@
|
||||
/** library to communicate using I²C as master
|
||||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* @date 2017-2021
|
||||
* @note the I²C peripheral is not well specified and does not cover all cases. The following complexity is the best I could do to cope with it
|
||||
*/
|
||||
|
||||
/* standard libraries */
|
||||
#include <stdint.h> // standard integer types
|
||||
#include <stdbool.h> // boolean types
|
||||
#include <stdlib.h> // general utilities
|
||||
|
||||
/* own libraries */
|
||||
#include "stm8s.h" // STM8S definitions
|
||||
#include "i2c_master.h" // I²C header and definitions
|
||||
|
||||
#include "main.h"
|
||||
|
||||
bool i2c_master_setup(bool fast)
|
||||
{
|
||||
// configure I²C peripheral
|
||||
if (!i2c_master_check_signals()) { // check the signal lines
|
||||
return false;
|
||||
}
|
||||
I2C_FREQR = 16; // the peripheral frequency (must match CPU frequency)
|
||||
if (fast) {
|
||||
I2C_CCRL = 0x2; // set SCL at 320 kHz (for less error)
|
||||
I2C_CCRH = (I2C_CCRH_FS | I2C_CCRH_DUTY); // set fast speed mode
|
||||
I2C_CCRH = (1U << 7); // set fast speed mode
|
||||
} else {
|
||||
I2C_CCRL = 0x50; // set SCL at 100 kHz
|
||||
I2C_CCRH = 0; // set standard speed mode
|
||||
}
|
||||
I2C_CR1 |= I2C_CR1_PE; // enable I²C peripheral
|
||||
return true;
|
||||
}
|
||||
|
||||
void i2c_master_release(void)
|
||||
{
|
||||
I2C_CR1 &= ~I2C_CR1_PE; // disable I²C peripheral
|
||||
}
|
||||
|
||||
bool i2c_master_check_signals(void)
|
||||
{
|
||||
i2c_master_release(); // ensure PB4/PB5 are not used as alternate function
|
||||
GPIO_PB->CR1.reg &= ~(PB4 | PB5); // operate in open-drain mode
|
||||
GPIO_PB->DDR.reg |= (PB4 | PB5); // set SCL/SDA as output to test pull-up
|
||||
GPIO_PB->ODR.reg |= PB4; // ensure SCL is high
|
||||
GPIO_PB->ODR.reg &= ~PB5; // set SDA low (start condition)
|
||||
for (volatile uint8_t t = 0; t < 10; t++); // wait 10 us
|
||||
GPIO_PB->ODR.reg |= PB5; // set SDA high (stop condition)
|
||||
for (volatile uint8_t t = 0; t < 10; t++); // wait 10 us for pull-up to take effect
|
||||
GPIO_PB->DDR.reg &= ~(PB4 | PB5); // set SCL/SDA as input before it is used as alternate function by the peripheral
|
||||
|
||||
return ((GPIO_PB->IDR.reg & PB4) && (GPIO_PB->IDR.reg & PB5)); // test if both lines are up
|
||||
}
|
||||
|
||||
void i2c_master_reset(void)
|
||||
{
|
||||
I2C_CR2 |= I2C_CR2_STOP; // release lines
|
||||
// don't check if BUSY is cleared since its state might be erroneous
|
||||
// rewriting I2C_CR2 before I2C_CR2_STOP is cleared might cause a second STOP, but at this point we don't care
|
||||
I2C_CR2 |= I2C_CR2_SWRST; // reset peripheral, in case we got stuck and the dog bit
|
||||
while ((0 == (GPIO_PB->IDR.reg & PB4) && (0 == (GPIO_PB->IDR.reg & PB5)))); // wait for SDA/SCL line to be released
|
||||
I2C_CR2 &= ~I2C_CR2_SWRST; // release reset
|
||||
I2C_CR1 &= ~I2C_CR1_PE; // disable I²C peripheral to clear some bits
|
||||
I2C_CR1 |= I2C_CR1_PE; // re-enable I²C peripheral (must be done before any other register is written)
|
||||
}
|
||||
|
||||
enum i2c_master_rc i2c_master_start(void)
|
||||
{
|
||||
// send (re-)start condition
|
||||
if (I2C_CR2 & (I2C_CR2_START | I2C_CR2_STOP)) { // ensure start or stop operations are not in progress
|
||||
return I2C_MASTER_RC_START_STOP_IN_PROGESS;
|
||||
}
|
||||
I2C_CR2 |= I2C_CR2_START; // sent start condition
|
||||
while ((I2C_CR2 & I2C_CR2_START) && !(I2C_SR2 & (I2C_SR2_BERR | I2C_SR2_ARLO)) && !(I2C_CR2 & I2C_CR2_STOP)); // wait until start condition has been accepted and cleared
|
||||
if (I2C_SR2 & (I2C_SR2_BERR | I2C_SR2_ARLO)) {
|
||||
return I2C_MASTER_RC_BUS_ERROR;
|
||||
}
|
||||
if (I2C_CR2 & I2C_CR2_STOP) {
|
||||
return I2C_MASTER_RC_TIMEOUT;
|
||||
}
|
||||
if (!(I2C_SR1 & I2C_SR1_SB)) { // the start bit has not been set
|
||||
return I2C_MASTER_RC_BUS_ERROR;
|
||||
}
|
||||
if (!(I2C_SR3 & I2C_SR3_MSL)) { // verify if in master mode
|
||||
return I2C_MASTER_RC_NOT_MASTER;
|
||||
}
|
||||
|
||||
return I2C_MASTER_RC_NONE;
|
||||
}
|
||||
|
||||
/** wait until stop is sent and bus is released
|
||||
* @return I²C return code
|
||||
*/
|
||||
static enum i2c_master_rc i2c_master_wait_stop(void)
|
||||
{
|
||||
while ((I2C_CR2 & I2C_CR2_STOP) && !(I2C_SR2 & (I2C_SR2_BERR | I2C_SR2_ARLO))); // wait until stop condition is accepted and cleared
|
||||
if (I2C_SR2 & (I2C_SR2_BERR | I2C_SR2_ARLO)) {
|
||||
return I2C_MASTER_RC_BUS_ERROR;
|
||||
}
|
||||
// this time we can't use I2C_CR2_STOP to check for timeout
|
||||
if (I2C_SR3 & I2C_SR3_MSL) { // ensure we are not in master mode anymore
|
||||
return I2C_MASTER_RC_BUS_ERROR;
|
||||
}
|
||||
if (I2C_SR3 & I2C_SR3_BUSY) { // ensure bus is released
|
||||
return I2C_MASTER_RC_BUS_ERROR;
|
||||
}
|
||||
if (!i2c_master_check_signals()) { // ensure lines are released
|
||||
return I2C_MASTER_RC_BUS_ERROR;
|
||||
}
|
||||
|
||||
return I2C_MASTER_RC_NONE;
|
||||
}
|
||||
|
||||
enum i2c_master_rc i2c_master_stop(void)
|
||||
{
|
||||
// sanity check
|
||||
if (!(I2C_SR3 & I2C_SR3_BUSY)) { // ensure bus is not already released
|
||||
return I2C_MASTER_RC_NONE; // bus has probably already been released
|
||||
}
|
||||
if (I2C_CR2 & (I2C_CR2_START | I2C_CR2_STOP)) { // ensure start or stop operations are not in progress
|
||||
return I2C_MASTER_RC_START_STOP_IN_PROGESS;
|
||||
}
|
||||
|
||||
I2C_CR2 |= I2C_CR2_STOP; // send stop to release bus
|
||||
return i2c_master_wait_stop();
|
||||
}
|
||||
|
||||
enum i2c_master_rc i2c_master_select_slave(uint16_t slave, bool address_10bit, bool write)
|
||||
{
|
||||
if (!(I2C_SR1 & I2C_SR1_SB)) { // start condition has not been sent yet
|
||||
enum i2c_master_rc rc = i2c_master_start(); // send start condition
|
||||
if (I2C_MASTER_RC_NONE != rc) {
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
if (!(I2C_SR3 & I2C_SR3_MSL)) { // I²C device is not in master mode
|
||||
return I2C_MASTER_RC_NOT_MASTER;
|
||||
}
|
||||
|
||||
// select slave
|
||||
I2C_SR2 &= ~(I2C_SR2_AF); // clear acknowledgement failure
|
||||
if (!address_10bit) { // 7-bit address
|
||||
I2C_DR = (slave << 1) | (write ? 0 : 1); // select slave, with read/write flag
|
||||
while (!(I2C_SR1 & I2C_SR1_ADDR) && !(I2C_SR2 & (I2C_SR2_AF | I2C_SR2_BERR | I2C_SR2_ARLO)) && !(I2C_CR2 & I2C_CR2_STOP)); // wait until address is transmitted (or error)
|
||||
if (I2C_SR2 & (I2C_SR2_BERR | I2C_SR2_ARLO)) {
|
||||
return I2C_MASTER_RC_BUS_ERROR;
|
||||
}
|
||||
if (I2C_CR2 & I2C_CR2_STOP) {
|
||||
return I2C_MASTER_RC_TIMEOUT;
|
||||
}
|
||||
if (I2C_SR2 & I2C_SR2_AF) { // address has not been acknowledged
|
||||
return I2C_MASTER_RC_NAK;
|
||||
}
|
||||
} else { // 10-bit address
|
||||
// send first part of address
|
||||
I2C_DR = 11110000 | (((slave >> 8 ) & 0x3) << 1); // send first header (11110xx0, where xx are 2 MSb of slave address)
|
||||
while (!(I2C_SR1 & I2C_SR1_ADD10) && !(I2C_SR2 & (I2C_SR2_AF | I2C_SR2_BERR | I2C_SR2_ARLO)) && !(I2C_CR2 & I2C_CR2_STOP)); // wait until address is transmitted (or error)
|
||||
if (I2C_SR2 & (I2C_SR2_BERR | I2C_SR2_ARLO)) {
|
||||
return I2C_MASTER_RC_BUS_ERROR;
|
||||
}
|
||||
if (I2C_CR2 & I2C_CR2_STOP) {
|
||||
return I2C_MASTER_RC_TIMEOUT;
|
||||
}
|
||||
if (I2C_SR2 & I2C_SR2_AF) { // address has not been acknowledged
|
||||
return I2C_MASTER_RC_NAK;
|
||||
}
|
||||
// send second part of address
|
||||
I2C_SR2 &= ~(I2C_SR2_AF); // clear acknowledgement failure
|
||||
I2C_DR = (slave & 0xff); // send remaining of address
|
||||
while (!(I2C_SR1 & I2C_SR1_ADDR) && !(I2C_SR2 & (I2C_SR2_AF | I2C_SR2_BERR | I2C_SR2_ARLO)) && !(I2C_CR2 & I2C_CR2_STOP)); // wait until address is transmitted (or error)
|
||||
if (I2C_SR2 & (I2C_SR2_BERR | I2C_SR2_ARLO)) {
|
||||
return I2C_MASTER_RC_BUS_ERROR;
|
||||
}
|
||||
if (I2C_CR2 & I2C_CR2_STOP) {
|
||||
return I2C_MASTER_RC_TIMEOUT;
|
||||
}
|
||||
if (I2C_SR2 & I2C_SR2_AF) { // address has not been acknowledged
|
||||
return I2C_MASTER_RC_NAK;
|
||||
}
|
||||
// go into receive mode if necessary
|
||||
if (!write) {
|
||||
enum i2c_master_rc rc = i2c_master_start(); // send start condition
|
||||
if (I2C_MASTER_RC_NONE != rc) {
|
||||
return rc;
|
||||
}
|
||||
// send first part of address with receive flag
|
||||
I2C_SR2 &= ~(I2C_SR2_AF); // clear acknowledgement failure
|
||||
I2C_DR = 11110001 | (((slave >> 8) & 0x3) << 1); // send header (11110xx1, where xx are 2 MSb of slave address)
|
||||
while (!(I2C_SR1 & I2C_SR1_ADDR) && !(I2C_SR2 & (I2C_SR2_AF | I2C_SR2_BERR | I2C_SR2_ARLO)) && !(I2C_CR2 & I2C_CR2_STOP)); // wait until address is transmitted (or error)
|
||||
if (I2C_SR2 & (I2C_SR2_BERR | I2C_SR2_ARLO)) {
|
||||
return I2C_MASTER_RC_BUS_ERROR;
|
||||
}
|
||||
if (I2C_CR2 & I2C_CR2_STOP) {
|
||||
return I2C_MASTER_RC_TIMEOUT;
|
||||
}
|
||||
if (I2C_SR2 & I2C_SR2_AF) { // address has not been acknowledged
|
||||
return I2C_MASTER_RC_NAK;
|
||||
}
|
||||
}
|
||||
}
|
||||
// verify if we are in the right mode
|
||||
if (write && !(I2C_SR3 & I2C_SR3_TRA)) {
|
||||
return I2C_MASTER_RC_NOT_TRANSMIT;
|
||||
} else if (!write && (I2C_SR3 & I2C_SR3_TRA)) {
|
||||
return I2C_MASTER_RC_NOT_RECEIVE;
|
||||
}
|
||||
return I2C_MASTER_RC_NONE;
|
||||
}
|
||||
|
||||
enum i2c_master_rc i2c_master_read(uint8_t* data, uint16_t data_size)
|
||||
{
|
||||
if (NULL == data || 0 == data_size) { // no data to read
|
||||
return I2C_MASTER_RC_OTHER; // we indicate an error because we don't send a stop
|
||||
}
|
||||
if (!(I2C_SR3 & I2C_SR3_MSL)) { // I²C device is not in master mode
|
||||
return I2C_MASTER_RC_NOT_MASTER;
|
||||
}
|
||||
// we can't check if the address phase it over since ADDR has been cleared when checking for mode
|
||||
if (I2C_SR3 & I2C_SR3_TRA) { // ensure we are in receive mode
|
||||
return I2C_MASTER_RC_NOT_RECEIVE;
|
||||
}
|
||||
|
||||
// read data
|
||||
for (uint16_t i = 0; i < data_size; i++) { // read bytes
|
||||
// set (N)ACK (EV6_3, EV6_1)
|
||||
if (1 == data_size - i) { // prepare to sent NACK for last byte
|
||||
I2C_CR2 &= ~(I2C_CR2_ACK); // disable ACK
|
||||
I2C_CR2 |= I2C_CR2_STOP; // prepare to send the stop
|
||||
} else {
|
||||
I2C_CR2 |= I2C_CR2_ACK; // enable ACK
|
||||
}
|
||||
while (!(I2C_SR1 & I2C_SR1_RXNE) && !(I2C_SR2 & (I2C_SR2_BERR | I2C_SR2_ARLO))); // wait until data is received (or error)
|
||||
if (I2C_SR2 & (I2C_SR2_BERR | I2C_SR2_ARLO)) {
|
||||
return I2C_MASTER_RC_BUS_ERROR;
|
||||
}
|
||||
data[i] = I2C_DR; // read the received byte
|
||||
}
|
||||
|
||||
return i2c_master_wait_stop();
|
||||
}
|
||||
|
||||
enum i2c_master_rc i2c_master_write(const uint8_t* data, uint16_t data_size)
|
||||
{
|
||||
if (NULL == data || 0 == data_size) { // no data to read
|
||||
return I2C_MASTER_RC_NONE; // we don't indicate an error because the stop is done seperatly
|
||||
}
|
||||
if (!(I2C_SR3 & I2C_SR3_MSL)) { // I²C device is not in master mode
|
||||
return I2C_MASTER_RC_NOT_MASTER;
|
||||
}
|
||||
// we can't check if the address phase it over since ADDR has been cleared when checking for mode
|
||||
if (!(I2C_SR3 & I2C_SR3_TRA)) { // ensure we are in transmit mode
|
||||
return I2C_MASTER_RC_NOT_TRANSMIT;
|
||||
}
|
||||
|
||||
// write data
|
||||
for (uint16_t i = 0; i < data_size; i++) { // write bytes
|
||||
I2C_SR2 &= ~(I2C_SR2_AF); // clear acknowledgement failure
|
||||
I2C_DR = data[i]; // send byte
|
||||
while (!(I2C_SR1 & I2C_SR1_TXE) && !(I2C_SR2 & (I2C_SR2_AF | I2C_SR2_BERR | I2C_SR2_ARLO)) && !(I2C_CR2 & I2C_CR2_STOP)); // wait until byte has been transmitted
|
||||
if (I2C_SR2 & (I2C_SR2_BERR | I2C_SR2_ARLO)) {
|
||||
return I2C_MASTER_RC_BUS_ERROR;
|
||||
}
|
||||
if (I2C_CR2 & I2C_CR2_STOP) {
|
||||
return I2C_MASTER_RC_TIMEOUT;
|
||||
}
|
||||
if (I2C_SR2 & I2C_SR2_AF) { // data has not been acknowledged
|
||||
return I2C_MASTER_RC_NAK;
|
||||
}
|
||||
}
|
||||
while (!(I2C_SR1 & I2C_SR1_BTF) && !(I2C_SR2 & (I2C_SR2_AF | I2C_SR2_BERR | I2C_SR2_ARLO)) && !(I2C_CR2 & I2C_CR2_STOP)); // wait until last byte has been transmitted
|
||||
if (I2C_SR2 & (I2C_SR2_BERR | I2C_SR2_ARLO)) {
|
||||
return I2C_MASTER_RC_BUS_ERROR;
|
||||
}
|
||||
if (I2C_CR2 & I2C_CR2_STOP) {
|
||||
return I2C_MASTER_RC_TIMEOUT;
|
||||
}
|
||||
if (I2C_SR2 & I2C_SR2_AF) { // data has not been acknowledged
|
||||
return I2C_MASTER_RC_NAK;
|
||||
}
|
||||
|
||||
return I2C_MASTER_RC_NONE;
|
||||
}
|
||||
|
||||
enum i2c_master_rc i2c_master_slave_read(uint16_t slave, bool address_10bit, uint8_t* data, uint16_t data_size)
|
||||
{
|
||||
enum i2c_master_rc rc = I2C_MASTER_RC_NONE; // to store I²C return codes
|
||||
rc = i2c_master_start(); // send (re-)start condition
|
||||
if (I2C_MASTER_RC_NONE != rc) {
|
||||
return rc;
|
||||
}
|
||||
rc = i2c_master_select_slave(slave, address_10bit, false); // select slave to read
|
||||
if (I2C_MASTER_RC_NONE != rc) {
|
||||
goto error;
|
||||
}
|
||||
if (NULL != data && data_size > 0) { // only read data if needed
|
||||
rc = i2c_master_read(data, data_size); // read data (includes stop)
|
||||
if (I2C_MASTER_RC_NONE != rc) {
|
||||
goto error;
|
||||
}
|
||||
} else {
|
||||
i2c_master_stop(); // sent stop condition
|
||||
}
|
||||
|
||||
rc = I2C_MASTER_RC_NONE; // all went well
|
||||
error:
|
||||
if (I2C_MASTER_RC_NONE != rc) {
|
||||
i2c_master_stop(); // sent stop condition
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
enum i2c_master_rc i2c_master_slave_write(uint16_t slave, bool address_10bit, const uint8_t* data, uint16_t data_size)
|
||||
{
|
||||
enum i2c_master_rc rc = I2C_MASTER_RC_NONE; // to store I²C return codes
|
||||
rc = i2c_master_start(); // send (re-)start condition
|
||||
if (I2C_MASTER_RC_NONE != rc) {
|
||||
return rc;
|
||||
}
|
||||
rc = i2c_master_select_slave(slave, address_10bit, true); // select slave to write
|
||||
if (I2C_MASTER_RC_NONE != rc) {
|
||||
goto error;
|
||||
}
|
||||
if (NULL != data && data_size > 0) { // write data only is some is available
|
||||
rc = i2c_master_write(data, data_size); // write data
|
||||
if (I2C_MASTER_RC_NONE != rc) {
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
rc = I2C_MASTER_RC_NONE; // all went well
|
||||
error:
|
||||
i2c_master_stop(); // sent stop condition
|
||||
return rc;
|
||||
}
|
||||
|
||||
enum i2c_master_rc i2c_master_address_read(uint16_t slave, bool address_10bit, const uint8_t* address, uint16_t address_size, uint8_t* data, uint16_t data_size)
|
||||
{
|
||||
enum i2c_master_rc rc = I2C_MASTER_RC_NONE; // to store I²C return codes
|
||||
rc = i2c_master_start(); // send (re-)start condition
|
||||
if (I2C_MASTER_RC_NONE != rc) {
|
||||
return rc;
|
||||
}
|
||||
rc = i2c_master_select_slave(slave, address_10bit, true); // select slave to write
|
||||
if (I2C_MASTER_RC_NONE != rc) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
// write address
|
||||
if (NULL != address && address_size > 0) {
|
||||
rc = i2c_master_write(address, address_size); // send memory address
|
||||
if (I2C_MASTER_RC_NONE != rc) {
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
// read data
|
||||
if (NULL != data && data_size > 0) {
|
||||
rc = i2c_master_start(); // send re-start condition
|
||||
if (I2C_MASTER_RC_NONE != rc) {
|
||||
return rc;
|
||||
}
|
||||
rc = i2c_master_select_slave(slave, address_10bit, false); // select slave to read
|
||||
if (I2C_MASTER_RC_NONE != rc) {
|
||||
goto error;
|
||||
}
|
||||
rc = i2c_master_read(data, data_size); // read memory (includes stop)
|
||||
if (I2C_MASTER_RC_NONE != rc) {
|
||||
goto error;
|
||||
}
|
||||
} else {
|
||||
i2c_master_stop(); // sent stop condition
|
||||
}
|
||||
|
||||
rc = I2C_MASTER_RC_NONE;
|
||||
error:
|
||||
if (I2C_MASTER_RC_NONE != rc) { // only send stop on error
|
||||
i2c_master_stop(); // sent stop condition
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
enum i2c_master_rc i2c_master_address_write(uint16_t slave, bool address_10bit, const uint8_t* address, uint16_t address_size, const uint8_t* data, uint16_t data_size)
|
||||
{
|
||||
if (UINT16_MAX - address_size < data_size) { // prevent integer overflow
|
||||
return I2C_MASTER_RC_OTHER;
|
||||
}
|
||||
if (address_size > 0 && NULL == address) {
|
||||
return I2C_MASTER_RC_OTHER;
|
||||
}
|
||||
if (data_size > 0 && NULL == data) {
|
||||
return I2C_MASTER_RC_OTHER;
|
||||
}
|
||||
|
||||
enum i2c_master_rc rc; // to store I²C return codes
|
||||
rc = i2c_master_start(); // send (re-)start condition
|
||||
if (I2C_MASTER_RC_NONE != rc) {
|
||||
return rc;
|
||||
}
|
||||
rc = i2c_master_select_slave(slave, address_10bit, true); // select slave to write
|
||||
if (I2C_MASTER_RC_NONE != rc) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (address_size && address) {
|
||||
rc = i2c_master_write(address, address_size); // send memory address
|
||||
if (I2C_MASTER_RC_NONE != rc) {
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
if (data_size && data) {
|
||||
rc = i2c_master_write(data, data_size); // send memory data
|
||||
if (I2C_MASTER_RC_NONE != rc) {
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
rc = I2C_MASTER_RC_NONE; // all went fine
|
||||
error:
|
||||
if (I2C_MASTER_RC_NONE != rc) {
|
||||
i2c_master_stop();
|
||||
} else {
|
||||
rc = i2c_master_stop(); // sent stop condition
|
||||
}
|
||||
return rc;
|
||||
}
|
112
i2c_master.h
Normal file
112
i2c_master.h
Normal file
@ -0,0 +1,112 @@
|
||||
/** library to communicate using I²C as master
|
||||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* @date 2017-2021
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
/** I²C return codes */
|
||||
enum i2c_master_rc {
|
||||
I2C_MASTER_RC_NONE = 0, /**< no error */
|
||||
I2C_MASTER_RC_START_STOP_IN_PROGESS, /**< a start or stop condition is already in progress */
|
||||
I2C_MASTER_RC_NOT_MASTER, /**< not in master mode */
|
||||
I2C_MASTER_RC_NOT_TRANSMIT, /**< not in transmit mode */
|
||||
I2C_MASTER_RC_NOT_RECEIVE, /**< not in receive mode */
|
||||
I2C_MASTER_RC_NOT_READY, /**< slave is not read (previous operations has been NACKed) */
|
||||
I2C_MASTER_RC_NAK, /**< not acknowledge received */
|
||||
I2C_MASTER_RC_BUS_ERROR, /**< an error on the I²C bus occurred */
|
||||
I2C_MASTER_RC_TIMEOUT, /**< a timeout has occurred because an operation has not completed in the expected time */
|
||||
I2C_MASTER_RC_OTHER, /** any other error (does not have to be I²C related) */
|
||||
};
|
||||
|
||||
/** setup I²C peripheral
|
||||
* @param[in] fast if Standard mode (Sm) is used for frequencies at 100 kHz, and Fast mode (Fm) is used for frequencies at 320 kHz
|
||||
* @return if I²C bus is ready to be used (same as i2c_master_check_signals)
|
||||
*/
|
||||
bool i2c_master_setup(bool fast);
|
||||
/** release I²C peripheral */
|
||||
void i2c_master_release(void);
|
||||
/** reset I²C peripheral, fixing any locked state
|
||||
* @warning the I²C peripheral needs to be re-setup
|
||||
* @note to be used after failed start or stop, and bus error
|
||||
*/
|
||||
void i2c_master_reset(void);
|
||||
/** check if SDA and SCL signals are pulled high
|
||||
* @return if SDA and SCL signals are pulled high
|
||||
*/
|
||||
bool i2c_master_check_signals(void);
|
||||
/** send start condition
|
||||
* @return I2C return code
|
||||
*/
|
||||
enum i2c_master_rc i2c_master_start(void);
|
||||
/** select I²C slave device
|
||||
* @warning a start condition should be sent before this operation
|
||||
* @param[in] slave I²C address of slave device to select
|
||||
* @param[in] address_10bit if the I²C slave address is 10 bits wide
|
||||
* @param[in] write this transaction will be followed by a read (false) or write (true) operation
|
||||
* @return I²C return code
|
||||
*/
|
||||
enum i2c_master_rc i2c_master_select_slave(uint16_t slave, bool address_10bit, bool write);
|
||||
/** read data over I²C
|
||||
* @param[out] data array to store bytes read
|
||||
* @param[in] data_size number of bytes to read
|
||||
* @return I²C return code
|
||||
* @warning the slave device must be selected before this operation
|
||||
* @note a stop condition will be sent at the end (I²C does not permit multiple reads, and this is necessary for 1-byte transfer)
|
||||
*/
|
||||
enum i2c_master_rc i2c_master_read(uint8_t* data, uint16_t data_size);
|
||||
/** write data over I²C
|
||||
* @param[in] data array of byte to write to slave
|
||||
* @param[in] data_size number of bytes to write
|
||||
* @return I²C return code
|
||||
* @warning the slave device must be selected before this operation
|
||||
* @note no stop condition is sent at the end, allowing multiple writes
|
||||
*/
|
||||
enum i2c_master_rc i2c_master_write(const uint8_t* data, uint16_t data_size);
|
||||
/** sent stop condition
|
||||
* @param[in] i2c I²C base address
|
||||
* @return I²C return code
|
||||
*/
|
||||
enum i2c_master_rc i2c_master_stop(void);
|
||||
/** read data from slave device
|
||||
* @param[in] slave I²C address of slave device to select
|
||||
* @param[in] address_10bit if the I²C slave address is 10 bits wide
|
||||
* @param[out] data array to store bytes read
|
||||
* @param[in] data_size number of bytes to read
|
||||
* @return I²C return code
|
||||
* @note start and stop conditions are included
|
||||
*/
|
||||
enum i2c_master_rc i2c_master_slave_read(uint16_t slave, bool address_10bit, uint8_t* data, uint16_t data_size);
|
||||
/** write data to slave device
|
||||
* @param[in] slave I²C address of slave device to select
|
||||
* @param[in] address_10bit if the I²C slave address is 10 bits wide
|
||||
* @param[in] data array of byte to write to slave
|
||||
* @param[in] data_size number of bytes to write
|
||||
* @return I²C return code
|
||||
* @note start and stop conditions are included
|
||||
*/
|
||||
enum i2c_master_rc i2c_master_slave_write(uint16_t slave, bool address_10bit, const uint8_t* data, uint16_t data_size);
|
||||
/** read data at specific address from an I²C memory slave
|
||||
* @param[in] slave I²C address of slave device to select
|
||||
* @param[in] address_10bit if the I²C slave address is 10 bits wide
|
||||
* @param[in] address memory address of slave to read from
|
||||
* @param[in] address_size address size in bytes
|
||||
* @param[out] data array to store bytes read
|
||||
* @param[in] data_size number of bytes to read
|
||||
* @return I²C return code
|
||||
* @note start and stop conditions are included
|
||||
*/
|
||||
enum i2c_master_rc i2c_master_address_read(uint16_t slave, bool address_10bit, const uint8_t* address, uint16_t address_size, uint8_t* data, uint16_t data_size);
|
||||
/** write data at specific address on an I²C memory slave
|
||||
* @param[in] slave I²C address of slave device to select
|
||||
* @param[in] address_10bit if the I²C slave address is 10 bits wide
|
||||
* @param[in] address memory address of slave to write to
|
||||
* @param[in] address_size address size in bytes
|
||||
* @param[in] data array of byte to write to slave
|
||||
* @param[in] data_size number of bytes to write
|
||||
* @return I²C return code
|
||||
* @note start and stop conditions are included
|
||||
*/
|
||||
enum i2c_master_rc i2c_master_address_write(uint16_t slave, bool address_10bit, const uint8_t* address, uint16_t address_size, const uint8_t* data, uint16_t data_size);
|
||||
|
Loading…
Reference in New Issue
Block a user