ArduinoCore-samd/libraries/Wire/Wire.cpp

378 lines
9.2 KiB
C++

/*
* TWI/I2C library for Arduino Zero
* Copyright (c) 2015 Arduino LLC. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
extern "C" {
#include <string.h>
}
#include <Arduino.h>
#include <wiring_private.h>
#ifdef USE_TINYUSB
// For Serial when selecting TinyUSB
#include <Adafruit_TinyUSB.h>
#endif
#include "Wire.h"
TwoWire::TwoWire(SERCOM * s, uint8_t pinSDA, uint8_t pinSCL)
{
this->sercom = s;
this->_uc_pinSDA=pinSDA;
this->_uc_pinSCL=pinSCL;
transmissionBegun = false;
}
void TwoWire::begin(void) {
//Master Mode
sercom->initMasterWIRE(TWI_CLOCK);
sercom->enableWIRE();
pinPeripheral(_uc_pinSDA, g_APinDescription[_uc_pinSDA].ulPinType);
pinPeripheral(_uc_pinSCL, g_APinDescription[_uc_pinSCL].ulPinType);
}
void TwoWire::begin(uint8_t address, bool enableGeneralCall) {
//Slave mode
sercom->initSlaveWIRE(address, enableGeneralCall);
sercom->enableWIRE();
pinPeripheral(_uc_pinSDA, g_APinDescription[_uc_pinSDA].ulPinType);
pinPeripheral(_uc_pinSCL, g_APinDescription[_uc_pinSCL].ulPinType);
}
void TwoWire::setClock(uint32_t baudrate) {
sercom->disableWIRE();
sercom->initMasterWIRE(baudrate);
sercom->enableWIRE();
}
void TwoWire::end() {
sercom->disableWIRE();
}
uint8_t TwoWire::requestFrom(uint8_t address, size_t quantity, bool stopBit)
{
if(quantity == 0)
{
return 0;
}
size_t byteRead = 0;
rxBuffer.clear();
if(sercom->startTransmissionWIRE(address, WIRE_READ_FLAG))
{
// Read first data
rxBuffer.store_char(sercom->readDataWIRE());
// Connected to slave
for (byteRead = 1; byteRead < quantity; ++byteRead)
{
sercom->prepareAckBitWIRE(); // Prepare Acknowledge
sercom->prepareCommandBitsWire(WIRE_MASTER_ACT_READ); // Prepare the ACK command for the slave
rxBuffer.store_char(sercom->readDataWIRE()); // Read data and send the ACK
}
sercom->prepareNackBitWIRE(); // Prepare NACK to stop slave transmission
//sercom->readDataWIRE(); // Clear data register to send NACK
if (stopBit)
{
sercom->prepareCommandBitsWire(WIRE_MASTER_ACT_STOP); // Send Stop
}
}
return byteRead;
}
uint8_t TwoWire::requestFrom(uint8_t address, size_t quantity)
{
return requestFrom(address, quantity, true);
}
void TwoWire::beginTransmission(uint8_t address) {
// save address of target and clear buffer
txAddress = address;
txBuffer.clear();
transmissionBegun = true;
}
// Errors:
// 0 : Success
// 1 : Data too long
// 2 : NACK on transmit of address
// 3 : NACK on transmit of data
// 4 : Other error
uint8_t TwoWire::endTransmission(bool stopBit)
{
transmissionBegun = false ;
// Start I2C transmission
if ( !sercom->startTransmissionWIRE( txAddress, WIRE_WRITE_FLAG ) )
{
sercom->prepareCommandBitsWire(WIRE_MASTER_ACT_STOP);
return 2 ; // Address error
}
// Send all buffer
while( txBuffer.available() )
{
// Trying to send data
if ( !sercom->sendDataMasterWIRE( txBuffer.read_char() ) )
{
sercom->prepareCommandBitsWire(WIRE_MASTER_ACT_STOP);
return 3 ; // Nack or error
}
}
if (stopBit)
{
sercom->prepareCommandBitsWire(WIRE_MASTER_ACT_STOP);
}
return 0;
}
uint8_t TwoWire::endTransmission()
{
return endTransmission(true);
}
size_t TwoWire::write(uint8_t ucData)
{
// No writing, without begun transmission or a full buffer
if ( !transmissionBegun || txBuffer.isFull() )
{
return 0 ;
}
txBuffer.store_char( ucData ) ;
return 1 ;
}
size_t TwoWire::write(const uint8_t *data, size_t quantity)
{
//Try to store all data
for(size_t i = 0; i < quantity; ++i)
{
//Return the number of data stored, when the buffer is full (if write return 0)
if(!write(data[i]))
return i;
}
//All data stored
return quantity;
}
int TwoWire::available(void)
{
return rxBuffer.available();
}
int TwoWire::read(void)
{
return rxBuffer.read_char();
}
int TwoWire::peek(void)
{
return rxBuffer.peek();
}
void TwoWire::flush(void)
{
// Do nothing, use endTransmission(..) to force
// data transfer.
}
void TwoWire::onReceive(void(*function)(int))
{
onReceiveCallback = function;
}
void TwoWire::onRequest(void(*function)(void))
{
onRequestCallback = function;
}
void TwoWire::onService(void)
{
if ( sercom->isSlaveWIRE() )
{
if(sercom->isStopDetectedWIRE() ||
(sercom->isAddressMatch() && sercom->isRestartDetectedWIRE() && !sercom->isMasterReadOperationWIRE())) //Stop or Restart detected
{
sercom->prepareAckBitWIRE();
sercom->prepareCommandBitsWire(0x03);
//Calling onReceiveCallback, if exists
if(onReceiveCallback)
{
onReceiveCallback(available());
}
rxBuffer.clear();
}
else if(sercom->isAddressMatch()) //Address Match
{
sercom->prepareAckBitWIRE();
sercom->prepareCommandBitsWire(0x03);
if(sercom->isMasterReadOperationWIRE()) //Is a request ?
{
txBuffer.clear();
transmissionBegun = true;
//Calling onRequestCallback, if exists
if(onRequestCallback)
{
onRequestCallback();
}
}
}
else if(sercom->isDataReadyWIRE())
{
if (sercom->isMasterReadOperationWIRE())
{
uint8_t c = 0xff;
if( txBuffer.available() ) {
c = txBuffer.read_char();
}
transmissionBegun = sercom->sendDataSlaveWIRE(c);
} else { //Received data
if (rxBuffer.isFull()) {
sercom->prepareNackBitWIRE();
} else {
//Store data
rxBuffer.store_char(sercom->readDataWIRE());
sercom->prepareAckBitWIRE();
}
sercom->prepareCommandBitsWire(0x03);
}
}
}
}
#if WIRE_INTERFACES_COUNT > 0
/* In case new variant doesn't define these macros,
* we put here the ones for Arduino Zero.
*
* These values should be different on some variants!
*/
#ifndef PERIPH_WIRE
#define PERIPH_WIRE sercom3
#define WIRE_IT_HANDLER SERCOM3_Handler
#endif // PERIPH_WIRE
TwoWire Wire(&PERIPH_WIRE, PIN_WIRE_SDA, PIN_WIRE_SCL);
void WIRE_IT_HANDLER(void) {
Wire.onService();
}
#if defined(__SAMD51__)
void WIRE_IT_HANDLER_0(void) { Wire.onService(); }
void WIRE_IT_HANDLER_1(void) { Wire.onService(); }
void WIRE_IT_HANDLER_2(void) { Wire.onService(); }
void WIRE_IT_HANDLER_3(void) { Wire.onService(); }
#endif // __SAMD51__
#endif
#if WIRE_INTERFACES_COUNT > 1
TwoWire Wire1(&PERIPH_WIRE1, PIN_WIRE1_SDA, PIN_WIRE1_SCL);
void WIRE1_IT_HANDLER(void) {
Wire1.onService();
}
#if defined(__SAMD51__)
void WIRE1_IT_HANDLER_0(void) { Wire1.onService(); }
void WIRE1_IT_HANDLER_1(void) { Wire1.onService(); }
void WIRE1_IT_HANDLER_2(void) { Wire1.onService(); }
void WIRE1_IT_HANDLER_3(void) { Wire1.onService(); }
#endif // __SAMD51__
#endif
#if WIRE_INTERFACES_COUNT > 2
TwoWire Wire2(&PERIPH_WIRE2, PIN_WIRE2_SDA, PIN_WIRE2_SCL);
void WIRE2_IT_HANDLER(void) {
Wire2.onService();
}
#if defined(__SAMD51__)
void WIRE2_IT_HANDLER_0(void) { Wire2.onService(); }
void WIRE2_IT_HANDLER_1(void) { Wire2.onService(); }
void WIRE2_IT_HANDLER_2(void) { Wire2.onService(); }
void WIRE2_IT_HANDLER_3(void) { Wire2.onService(); }
#endif // __SAMD51__
#endif
#if WIRE_INTERFACES_COUNT > 3
TwoWire Wire3(&PERIPH_WIRE3, PIN_WIRE3_SDA, PIN_WIRE3_SCL);
void WIRE3_IT_HANDLER(void) {
Wire3.onService();
}
#if defined(__SAMD51__)
void WIRE3_IT_HANDLER_0(void) { Wire3.onService(); }
void WIRE3_IT_HANDLER_1(void) { Wire3.onService(); }
void WIRE3_IT_HANDLER_2(void) { Wire3.onService(); }
void WIRE3_IT_HANDLER_3(void) { Wire3.onService(); }
#endif // __SAMD51__
#endif
#if WIRE_INTERFACES_COUNT > 4
TwoWire Wire4(&PERIPH_WIRE4, PIN_WIRE4_SDA, PIN_WIRE4_SCL);
void WIRE4_IT_HANDLER(void) {
Wire4.onService();
}
#if defined(__SAMD51__)
void WIRE4_IT_HANDLER_0(void) { Wire4.onService(); }
void WIRE4_IT_HANDLER_1(void) { Wire4.onService(); }
void WIRE4_IT_HANDLER_2(void) { Wire4.onService(); }
void WIRE4_IT_HANDLER_3(void) { Wire4.onService(); }
#endif // __SAMD51__
#endif
#if WIRE_INTERFACES_COUNT > 5
TwoWire Wire5(&PERIPH_WIRE5, PIN_WIRE5_SDA, PIN_WIRE5_SCL);
void WIRE5_IT_HANDLER(void) {
Wire5.onService();
}
#if defined(__SAMD51__)
void WIRE5_IT_HANDLER_0(void) { Wire5.onService(); }
void WIRE5_IT_HANDLER_1(void) { Wire5.onService(); }
void WIRE5_IT_HANDLER_2(void) { Wire5.onService(); }
void WIRE5_IT_HANDLER_3(void) { Wire5.onService(); }
#endif // __SAMD51__
#endif