Merge branch 'lacklustrlabs-WireSlave'
This commit is contained in:
commit
5368d04c56
|
@ -0,0 +1 @@
|
|||
#error "Something is trying to include Wire_slave.h when Wire.h is already included, they are mutually exclusive"
|
|
@ -0,0 +1,10 @@
|
|||
name=Wire
|
||||
version=1.0
|
||||
author=Roger Clark
|
||||
maintainer=
|
||||
sentence=Allows the communication between devices or sensors connected via Two Wire Interface Bus.
|
||||
paragraph=
|
||||
category=Communication
|
||||
url=http://www.arduino.cc/en/Reference/Wire
|
||||
architectures=STM32F1
|
||||
include=Wire.h,SoftWire.h
|
|
@ -0,0 +1,87 @@
|
|||
// I2C SRF10 or SRF08 Devantech Ultrasonic Ranger Finder
|
||||
// by Nicholas Zambetti <http://www.zambetti.com>
|
||||
// and James Tichenor <http://www.jamestichenor.net>
|
||||
|
||||
// Demonstrates use of the Wire library reading data from the
|
||||
// Devantech Utrasonic Rangers SFR08 and SFR10
|
||||
|
||||
// Created 29 April 2006
|
||||
|
||||
// This example code is in the public domain.
|
||||
|
||||
|
||||
#include <Wire_slave.h>
|
||||
|
||||
void setup()
|
||||
{
|
||||
Wire.begin(); // join i2c bus (address optional for master)
|
||||
Serial.begin(115200); // start serial communication at 9600bps
|
||||
}
|
||||
|
||||
int reading = 0;
|
||||
|
||||
void loop()
|
||||
{
|
||||
// step 1: instruct sensor to read echoes
|
||||
Wire.beginTransmission(112); // transmit to device #112 (0x70)
|
||||
// the address specified in the datasheet is 224 (0xE0)
|
||||
// but i2c adressing uses the high 7 bits so it's 112
|
||||
Wire.write(byte(0x00)); // sets register pointer to the command register (0x00)
|
||||
Wire.write(byte(0x50)); // command sensor to measure in "inches" (0x50)
|
||||
// use 0x51 for centimeters
|
||||
// use 0x52 for ping microseconds
|
||||
Wire.endTransmission(); // stop transmitting
|
||||
|
||||
// step 2: wait for readings to happen
|
||||
delay(70); // datasheet suggests at least 65 milliseconds
|
||||
|
||||
// step 3: instruct sensor to return a particular echo reading
|
||||
Wire.beginTransmission(112); // transmit to device #112
|
||||
Wire.write(byte(0x02)); // sets register pointer to echo #1 register (0x02)
|
||||
Wire.endTransmission(); // stop transmitting
|
||||
|
||||
// step 4: request reading from sensor
|
||||
Wire.requestFrom(112, 2); // request 2 bytes from slave device #112
|
||||
|
||||
// step 5: receive reading from sensor
|
||||
if(2 <= Wire.available()) // if two bytes were received
|
||||
{
|
||||
reading = Wire.read(); // receive high byte (overwrites previous reading)
|
||||
reading = reading << 8; // shift high byte to be high 8 bits
|
||||
reading |= Wire.read(); // receive low byte as lower 8 bits
|
||||
Serial.println(reading); // print the reading
|
||||
}
|
||||
|
||||
delay(250); // wait a bit since people have to read the output :)
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
|
||||
// The following code changes the address of a Devantech Ultrasonic Range Finder (SRF10 or SRF08)
|
||||
// usage: changeAddress(0x70, 0xE6);
|
||||
|
||||
void changeAddress(byte oldAddress, byte newAddress)
|
||||
{
|
||||
Wire.beginTransmission(oldAddress);
|
||||
Wire.write(byte(0x00));
|
||||
Wire.write(byte(0xA0));
|
||||
Wire.endTransmission();
|
||||
|
||||
Wire.beginTransmission(oldAddress);
|
||||
Wire.write(byte(0x00));
|
||||
Wire.write(byte(0xAA));
|
||||
Wire.endTransmission();
|
||||
|
||||
Wire.beginTransmission(oldAddress);
|
||||
Wire.write(byte(0x00));
|
||||
Wire.write(byte(0xA5));
|
||||
Wire.endTransmission();
|
||||
|
||||
Wire.beginTransmission(oldAddress);
|
||||
Wire.write(byte(0x00));
|
||||
Wire.write(newAddress);
|
||||
Wire.endTransmission();
|
||||
}
|
||||
|
||||
*/
|
|
@ -0,0 +1,38 @@
|
|||
// I2C Digital Potentiometer
|
||||
// by Nicholas Zambetti <http://www.zambetti.com>
|
||||
// and Shawn Bonkowski <http://people.interaction-ivrea.it/s.bonkowski/>
|
||||
|
||||
// Demonstrates use of the Wire library
|
||||
// Controls AD5171 digital potentiometer via I2C/TWI
|
||||
|
||||
// Created 31 March 2006
|
||||
|
||||
// This example code is in the public domain.
|
||||
|
||||
// This example code is in the public domain.
|
||||
|
||||
|
||||
#include <Wire_slave.h>
|
||||
|
||||
void setup()
|
||||
{
|
||||
Wire.begin(); // join i2c bus (address optional for master)
|
||||
}
|
||||
|
||||
byte val = 0;
|
||||
|
||||
void loop()
|
||||
{
|
||||
Wire.beginTransmission(44); // transmit to device #44 (0x2c)
|
||||
// device address is specified in datasheet
|
||||
Wire.write(byte(0x00)); // sends instruction byte
|
||||
Wire.write(val); // sends potentiometer value byte
|
||||
Wire.endTransmission(); // stop transmitting
|
||||
|
||||
val++; // increment value
|
||||
if(val == 64) // if reached 64th position (max)
|
||||
{
|
||||
val = 0; // start over from lowest value
|
||||
}
|
||||
delay(500);
|
||||
}
|
|
@ -0,0 +1,118 @@
|
|||
/*
|
||||
* i2c_slave example.cpp
|
||||
*
|
||||
* You can use this sketch in combination with master_writer.pde
|
||||
*
|
||||
* Created on: 4 Sep 2012
|
||||
* Author: Barry Carter <barry.carter@gmail.com>
|
||||
*/
|
||||
#include <Wire_slave.h>
|
||||
#include <Arduino.h>
|
||||
|
||||
#define USE_BUFFERED_EXAMPLE 1
|
||||
|
||||
i2c_msg msg;
|
||||
uint8 buffer[255];
|
||||
char* const bufferAsChar = (char*)buffer; // ready type casted alias
|
||||
|
||||
volatile bool newMessage = false;
|
||||
|
||||
void funcrx(i2c_msg *msg __attribute__((unused))){
|
||||
// Received length will be in msg->length
|
||||
newMessage = true;
|
||||
}
|
||||
|
||||
#if USE_BUFFERED_EXAMPLE == 1
|
||||
/* We ARE using a buffer to transmit the data out.
|
||||
* Make sure you fill the buffer with the data AND you set the length correctly
|
||||
*/
|
||||
void functx(i2c_msg *msg){
|
||||
// Cheeky. We are using the received byte of the data which is currently in
|
||||
// byte 0 to echo it back to the master device
|
||||
//msg->data[0] = 0x01; // We are re-using the rx buffer here to echo the request back
|
||||
msg->data[1] = 0x02;
|
||||
msg->data[2] = 0x03;
|
||||
msg->data[3] = 0x04;
|
||||
msg->data[4] = 0x05;
|
||||
msg->length = 5;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
/* We are NOT using the buffered data transmission
|
||||
* We will get this callback for each outgoing packet. Make sure to call i2c_write
|
||||
* Strickly speaking, we should be sending a NACk on the last byte we want to send
|
||||
* but for this test example I am going to assume the master will NACK it when it
|
||||
* wants to stop.
|
||||
*/
|
||||
void functx(i2c_msg *msg){
|
||||
i2c_write(I2C1, msg->data[0]);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
Serial.println("libmaple I2C slave reader example");
|
||||
|
||||
// attach the buffer
|
||||
msg.data = buffer;
|
||||
|
||||
/* Init slave mode. Enables master too
|
||||
* We are going to configure the slave device to
|
||||
* - enable fast I2C (400khz)
|
||||
* - dual addresses (can have 2 addresses per module)
|
||||
* general call (accepts data writes to 0x00 on a broadcast basis)
|
||||
*
|
||||
* If the buffered example is enabled, then we also enable the
|
||||
* buffer for rx and tx.
|
||||
* Note you can independently enable/disable RX and TX buffers to
|
||||
* allow a buffered read and direct writes. Useful if you don't know how
|
||||
* much the master will read.
|
||||
*/
|
||||
#if USE_BUFFERED_EXAMPLE == 1
|
||||
i2c_slave_enable(I2C1, I2C_FAST_MODE | I2C_SLAVE_DUAL_ADDRESS | I2C_SLAVE_GENERAL_CALL | I2C_SLAVE_USE_RX_BUFFER | I2C_SLAVE_USE_TX_BUFFER);
|
||||
#else
|
||||
i2c_slave_enable(I2C1, I2C_FAST_MODE | I2C_SLAVE_DUAL_ADDRESS | I2C_SLAVE_GENERAL_CALL);
|
||||
#endif
|
||||
|
||||
// attach receive handler
|
||||
i2c_slave_attach_recv_handler(I2C1, &msg, funcrx);
|
||||
// attach transmit handler
|
||||
i2c_slave_attach_transmit_handler(I2C1, &msg, functx);
|
||||
|
||||
// set addresss to 4
|
||||
i2c_slave_set_own_address(I2C1, 4);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
static uint32_t lastMessage = millis();
|
||||
|
||||
// This is potentially dangerous.
|
||||
// We're reading from the live buffer, the content can change
|
||||
// in the middle of Serial.println
|
||||
|
||||
if (newMessage && strlen(bufferAsChar)==6) {
|
||||
for (int i=0; i<5; i++) {
|
||||
// Print as char
|
||||
Serial.print(bufferAsChar[i]);
|
||||
}
|
||||
// Print as byte
|
||||
Serial.println(buffer[5]);
|
||||
lastMessage = millis();
|
||||
newMessage = false;
|
||||
} else {
|
||||
if(newMessage && strlen(bufferAsChar)!=6) {
|
||||
// this also happends on the line "x is 0"
|
||||
Serial.print("Bad data received:");
|
||||
Serial.println(bufferAsChar);
|
||||
Serial.print("strlen:");
|
||||
Serial.println(strlen(bufferAsChar));
|
||||
newMessage = false;
|
||||
} else
|
||||
if(millis() - lastMessage > 3000){
|
||||
Serial.println("Nothing received in 3 seconds.");
|
||||
lastMessage = millis();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
// --------------------------------------
|
||||
// i2c_scanner
|
||||
//
|
||||
// Version 1
|
||||
// This program (or code that looks like it)
|
||||
// can be found in many places.
|
||||
// For example on the Arduino.cc forum.
|
||||
// The original author is not know.
|
||||
// Version 2, Juni 2012, Using Arduino 1.0.1
|
||||
// Adapted to be as simple as possible by Arduino.cc user Krodal
|
||||
// Version 3, Feb 26 2013
|
||||
// V3 by louarnold
|
||||
// Version 4, March 3, 2013, Using Arduino 1.0.3
|
||||
// by Arduino.cc user Krodal.
|
||||
// Changes by louarnold removed.
|
||||
// Scanning addresses changed from 0...127 to 1...119,
|
||||
// according to the i2c scanner by Nick Gammon
|
||||
// http://www.gammon.com.au/forum/?id=10896
|
||||
// Version 5, March 28, 2013
|
||||
// As version 4, but address scans now to 127.
|
||||
// A sensor seems to use address 120.
|
||||
//
|
||||
// This sketch tests the standard 7-bit addresses
|
||||
// Devices with higher bit address might not be seen properly.
|
||||
//
|
||||
|
||||
#include <Wire_slave.h>
|
||||
|
||||
//use IIC2
|
||||
//TwoWire WIRE2 (2,I2C_FAST_MODE);
|
||||
//#define Wire WIRE2
|
||||
|
||||
|
||||
void setup() {
|
||||
|
||||
Serial.begin(115200);
|
||||
Wire.begin();
|
||||
Serial.println("\nI2C Scanner");
|
||||
}
|
||||
|
||||
|
||||
void loop() {
|
||||
byte error, address;
|
||||
int nDevices;
|
||||
|
||||
Serial.println("Scanning...");
|
||||
|
||||
nDevices = 0;
|
||||
for(address = 1; address < 127; address++) {
|
||||
// The i2c_scanner uses the return value of
|
||||
// the Write.endTransmisstion to see if
|
||||
// a device did acknowledge to the address.
|
||||
|
||||
Wire.beginTransmission(address);
|
||||
error = Wire.endTransmission();
|
||||
|
||||
if (error == 0) {
|
||||
Serial.print("I2C device found at address 0x");
|
||||
if (address < 16)
|
||||
Serial.print("0");
|
||||
Serial.println(address, HEX);
|
||||
|
||||
nDevices++;
|
||||
}
|
||||
else if (error == 4) {
|
||||
Serial.print("Unknown error at address 0x");
|
||||
if (address < 16)
|
||||
Serial.print("0");
|
||||
Serial.println(address, HEX);
|
||||
}
|
||||
}
|
||||
if (nDevices == 0)
|
||||
Serial.println("No I2C devices found");
|
||||
else
|
||||
Serial.println("done");
|
||||
|
||||
delay(5000); // wait 5 seconds for next scan
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
// Wire Master Reader
|
||||
// by Nicholas Zambetti <http://www.zambetti.com>
|
||||
|
||||
// Demonstrates use of the Wire library
|
||||
// Reads data from an I2C/TWI slave device
|
||||
// Refer to the "Wire Slave Sender" example for use with this
|
||||
|
||||
// Created 29 March 2006
|
||||
|
||||
// This example code is in the public domain.
|
||||
|
||||
|
||||
#include <Wire_slave.h>
|
||||
|
||||
void setup()
|
||||
{
|
||||
Wire.begin(); // join i2c bus (address optional for master)
|
||||
Serial.begin(115200); // start serial for output
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
Wire.requestFrom(8, 6); // request 6 bytes from slave device #8
|
||||
|
||||
while(Wire.available()) // slave may send less than requested
|
||||
{
|
||||
char c = Wire.read(); // receive a byte as character
|
||||
Serial.print(c); // print the character
|
||||
}
|
||||
|
||||
delay(500);
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
// Wire Master Writer
|
||||
// by Nicholas Zambetti <http://www.zambetti.com>
|
||||
|
||||
// Demonstrates use of the Wire library
|
||||
// Writes data to an I2C/TWI slave device
|
||||
// Refer to the "Wire Slave Receiver" example for use with this
|
||||
|
||||
// Created 29 March 2006
|
||||
|
||||
// This example code is in the public domain.
|
||||
|
||||
|
||||
#include <Wire_slave.h>
|
||||
|
||||
void setup()
|
||||
{
|
||||
Wire.begin(); // join i2c bus (address optional for master)
|
||||
}
|
||||
|
||||
byte x = 0;
|
||||
|
||||
void loop()
|
||||
{
|
||||
Wire.beginTransmission(4); // transmit to device #4
|
||||
Wire.write("x is "); // sends five bytes
|
||||
Wire.write(x); // sends one byte
|
||||
Wire.endTransmission(); // stop transmitting
|
||||
|
||||
x++;
|
||||
delay(500);
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
// Wire Slave Receiver
|
||||
// by Nicholas Zambetti <http://www.zambetti.com>
|
||||
|
||||
// Demonstrates use of the Wire library
|
||||
// Receives data as an I2C/TWI slave device
|
||||
// Refer to the "Wire Master Writer" example for use with this
|
||||
|
||||
// Created 29 March 2006
|
||||
|
||||
// This example code is in the public domain.
|
||||
|
||||
#include <Wire_slave.h>
|
||||
//#define Serial Serial1
|
||||
|
||||
#define MasterWire Wire
|
||||
#define SlaveWire Wire1
|
||||
#define ENABLE_SLAVE
|
||||
#define ENABLE_MASTER
|
||||
|
||||
void receiveEvent(int howMany);
|
||||
|
||||
void setup(){
|
||||
Serial.begin(115200); // start serial for output
|
||||
|
||||
#ifdef ENABLE_MASTER
|
||||
MasterWire.begin(); // join i2c bus #1 as master
|
||||
Serial.println("Done with MasterWire.begin ");
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_SLAVE
|
||||
SlaveWire.begin(4); // join i2c bus #2 as slave with address #4
|
||||
Serial.println("Done with SlaveWire.begin ");
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_SLAVE
|
||||
SlaveWire.onReceive(receiveEvent); // register event
|
||||
#endif
|
||||
}
|
||||
|
||||
void loop(){
|
||||
#ifdef ENABLE_MASTER
|
||||
static byte x = 0;
|
||||
Serial.print("Master writing ");
|
||||
Serial.println(x);
|
||||
|
||||
MasterWire.beginTransmission(4); // transmit to device #4
|
||||
MasterWire.write("x is "); // sends five bytes
|
||||
MasterWire.write(x); // sends one byte
|
||||
MasterWire.endTransmission(); // stop transmitting
|
||||
|
||||
x++;
|
||||
#endif
|
||||
delay(500);
|
||||
}
|
||||
|
||||
// function that executes whenever data is received from master
|
||||
// this function is registered as an event, see setup()
|
||||
// Note that it is not advicable to call Serial.print() from within an ISR
|
||||
void receiveEvent(int howMany){
|
||||
//Serial.print("Slave receving ");
|
||||
while(1 < SlaveWire.available()){ // loop through all but the last
|
||||
char c = SlaveWire.read(); // receive byte as a character
|
||||
Serial.print(c); // print the character
|
||||
}
|
||||
int x = SlaveWire.read(); // receive byte as an integer
|
||||
Serial.println(x); // print the integer
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
// Wire Slave Receiver
|
||||
// by Nicholas Zambetti <http://www.zambetti.com>
|
||||
|
||||
// Demonstrates use of the Wire library
|
||||
// Receives data as an I2C/TWI slave device
|
||||
// Refer to the "Wire Master Writer" example for use with this
|
||||
|
||||
// Created 29 March 2006
|
||||
|
||||
// This example code is in the public domain.
|
||||
|
||||
// The code is temporarily moved to the code.cpp.
|
||||
// This makes it possible to set breakpoints in eclipse IDE.
|
||||
// I'm sure that there is an Eclipse configuration that makes it
|
||||
// possible for it to detect .ino files as .cpp, but I'm lazy
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
// Wire Slave Receiver
|
||||
// by Nicholas Zambetti <http://www.zambetti.com>
|
||||
|
||||
// Demonstrates use of the Wire library
|
||||
// Receives data as an I2C/TWI slave device
|
||||
// Refer to the "Wire Master Writer" example for use with this
|
||||
|
||||
// Created 29 March 2006
|
||||
|
||||
// This example code is in the public domain.
|
||||
|
||||
|
||||
#include <Wire_slave.h>
|
||||
|
||||
void setup(){
|
||||
Serial.begin(115200); // start serial for output
|
||||
|
||||
Wire.begin(4); // join i2c bus with address #4
|
||||
Wire.onReceive(receiveEvent); // register event
|
||||
}
|
||||
|
||||
void loop(){
|
||||
delay(100);
|
||||
}
|
||||
|
||||
// function that executes whenever data is received from master
|
||||
// this function is registered as an event, see setup()
|
||||
// Note that it is not advicable to call Serial.print() from within an ISR
|
||||
void receiveEvent(int howMany){
|
||||
while(1 < Wire.available()){ // loop through all but the last
|
||||
char c = Wire.read(); // receive byte as a character
|
||||
Serial.print(c); // print the character
|
||||
}
|
||||
int x = Wire.read(); // receive byte as an integer
|
||||
Serial.println(x); // print the integer
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
// Wire Slave Sender
|
||||
// by Nicholas Zambetti <http://www.zambetti.com>
|
||||
|
||||
// Demonstrates use of the Wire library
|
||||
// Sends data as an I2C/TWI slave device
|
||||
// Refer to the "Wire Master Reader" example for use with this
|
||||
|
||||
// Created 29 March 2006
|
||||
|
||||
// This example code is in the public domain.
|
||||
|
||||
|
||||
#include <Wire_slave.h>
|
||||
|
||||
void setup()
|
||||
{
|
||||
Wire.begin(8); // join i2c bus with address #8
|
||||
Wire.onRequest(requestEvent); // register event
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
delay(100);
|
||||
}
|
||||
|
||||
// function that executes whenever data is requested by master
|
||||
// this function is registered as an event, see setup()
|
||||
void requestEvent()
|
||||
{
|
||||
Wire.write("hello "); // respond with message of 6 bytes
|
||||
// as expected by master
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
#######################################
|
||||
# Syntax Coloring Map For Wire
|
||||
#######################################
|
||||
|
||||
#######################################
|
||||
# Datatypes (KEYWORD1)
|
||||
#######################################
|
||||
|
||||
#######################################
|
||||
# Methods and Functions (KEYWORD2)
|
||||
#######################################
|
||||
|
||||
begin KEYWORD2
|
||||
setClock KEYWORD2
|
||||
beginTransmission KEYWORD2
|
||||
endTransmission KEYWORD2
|
||||
requestFrom KEYWORD2
|
||||
onReceive KEYWORD2
|
||||
onRequest KEYWORD2
|
||||
|
||||
#######################################
|
||||
# Instances (KEYWORD2)
|
||||
#######################################
|
||||
|
||||
Wire KEYWORD2
|
||||
Wire1 KEYWORD2
|
||||
|
||||
#######################################
|
||||
# Constants (LITERAL1)
|
||||
#######################################
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
name=Wire_Slave
|
||||
version=1.0
|
||||
author=
|
||||
email=
|
||||
sentence=Wire with slave support
|
||||
paragraph=Experimental Wire with slave support
|
||||
url=
|
||||
architectures=STM32F1
|
||||
maintainer=
|
||||
category=Communication
|
||||
include=src/Wire_slave.h
|
|
@ -0,0 +1,4 @@
|
|||
#pragma once
|
||||
#include "Wire_slave.h"
|
||||
|
||||
//#error "Something is trying to include Wire.h when Wire_slave.h is already included, they are mutually exclusive"
|
|
@ -0,0 +1,524 @@
|
|||
/*
|
||||
TwoWire.cpp - TWI/I2C library for Wiring & Arduino
|
||||
Copyright (c) 2006 Nicholas Zambetti. All right 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
|
||||
|
||||
Modified 2012 by Todd Krein (todd@krein.org) to implement repeated starts
|
||||
*/
|
||||
|
||||
extern "C" {
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
}
|
||||
|
||||
#include "Wire_slave.h"
|
||||
#include "wirish.h"
|
||||
#include <libmaple/i2c_slave.h>
|
||||
|
||||
#define BUFFER_LENGTH 32
|
||||
|
||||
#define MASTER_ADDRESS 0x33
|
||||
// Constructors ////////////////////////////////////////////////////////////////
|
||||
|
||||
TwoWire::TwoWire(i2c_dev* i2cDevice) :
|
||||
sel_hard(i2cDevice),
|
||||
rxBuffer(nullptr),
|
||||
rxBufferAllocated(0),
|
||||
rxBufferIndex(0),
|
||||
rxBufferLength(0),
|
||||
txBuffer(nullptr),
|
||||
txBufferAllocated(0),
|
||||
txBufferIndex(0),
|
||||
txBufferLength(0),
|
||||
transmitting(false),
|
||||
master(true),
|
||||
dev_flags(0),
|
||||
itc_msg(),
|
||||
itc_slave_msg(),
|
||||
user_onRequest(nullptr),
|
||||
user_onReceive(nullptr) {
|
||||
}
|
||||
|
||||
// Public Methods //////////////////////////////////////////////////////////////
|
||||
|
||||
void TwoWire::begin(void) {
|
||||
begin(MASTER_ADDRESS);
|
||||
}
|
||||
|
||||
void TwoWire::begin(uint8_t address){
|
||||
|
||||
rxBufferIndex = 0;
|
||||
rxBufferLength = 0;
|
||||
allocateRxBuffer(BUFFER_LENGTH);
|
||||
|
||||
txBufferIndex = 0;
|
||||
txBufferLength = 0;
|
||||
allocateTxBuffer(BUFFER_LENGTH);
|
||||
|
||||
transmitting = false;
|
||||
|
||||
master = (address == MASTER_ADDRESS);
|
||||
|
||||
// Set default speed to 100KHz
|
||||
if (master) {
|
||||
dev_flags = 0;
|
||||
i2c_master_enable(sel_hard, dev_flags);
|
||||
} else {
|
||||
// TODO: I2C_SLAVE_DUAL_ADDRESS ?
|
||||
dev_flags = I2C_SLAVE_GENERAL_CALL | I2C_SLAVE_USE_RX_BUFFER |
|
||||
I2C_SLAVE_USE_TX_BUFFER;
|
||||
|
||||
itc_slave_msg.addr = address;
|
||||
itc_slave_msg.flags = 0;
|
||||
itc_slave_msg.data = rxBuffer;
|
||||
itc_slave_msg.length = 0;
|
||||
itc_slave_msg.flags = 0;
|
||||
|
||||
// TODO why does enable only work before setting IRS and address?
|
||||
i2c_slave_enable(sel_hard, dev_flags);
|
||||
|
||||
if (sel_hard==I2C1){
|
||||
// attach receive handler
|
||||
i2c_slave_attach_recv_handler(sel_hard, &itc_slave_msg, onReceiveService1);
|
||||
// attach transmit handler
|
||||
i2c_slave_attach_transmit_handler(sel_hard, &itc_slave_msg, onRequestService1);
|
||||
}
|
||||
#if WIRE_INTERFACES_COUNT > 1
|
||||
else if (sel_hard==I2C2){
|
||||
// attach receive handler
|
||||
i2c_slave_attach_recv_handler(sel_hard, &itc_slave_msg, onReceiveService2);
|
||||
// attach transmit handler
|
||||
i2c_slave_attach_transmit_handler(sel_hard, &itc_slave_msg, onRequestService2);
|
||||
}
|
||||
#endif
|
||||
|
||||
i2c_slave_set_own_address(sel_hard, address);
|
||||
}
|
||||
}
|
||||
|
||||
void TwoWire::begin(int address) {
|
||||
begin((uint8_t) address);
|
||||
}
|
||||
|
||||
void TwoWire::end(void) {
|
||||
free(txBuffer);
|
||||
txBuffer = nullptr;
|
||||
txBufferAllocated = 0;
|
||||
free(rxBuffer);
|
||||
rxBuffer = nullptr;
|
||||
rxBufferAllocated = 0;
|
||||
i2c_peripheral_disable(sel_hard);
|
||||
i2c_master_release_bus(sel_hard); // TODO is this required?
|
||||
}
|
||||
|
||||
void TwoWire::setClock(uint32_t frequencyHz) {
|
||||
switch (frequencyHz) {
|
||||
case 400000:
|
||||
dev_flags |= I2C_FAST_MODE; // set FAST_MODE bit
|
||||
break;
|
||||
case 100000:
|
||||
default:
|
||||
dev_flags &= ~I2C_FAST_MODE; // clear FAST_MODE bit
|
||||
break;
|
||||
}
|
||||
if (sel_hard->regs->CR1 & I2C_CR1_PE){
|
||||
i2c_disable(sel_hard);
|
||||
i2c_master_enable(sel_hard, dev_flags);
|
||||
}
|
||||
}
|
||||
|
||||
uint8 TwoWire::process(bool stop) {
|
||||
int8 res = i2c_master_xfer(sel_hard, &itc_msg, 1, 0);
|
||||
|
||||
if (res == I2C_ERROR_PROTOCOL) {
|
||||
if (sel_hard->error_flags & I2C_SR1_AF) { /* NACK */
|
||||
res = (sel_hard->error_flags & I2C_SR1_ADDR ? ENACKADDR : ENACKTRNS);
|
||||
} else if (sel_hard->error_flags & I2C_SR1_OVR) { /* Over/Underrun */
|
||||
res = EDATA;
|
||||
} else { /* Bus or Arbitration error */
|
||||
res = EOTHER;
|
||||
}
|
||||
i2c_disable(sel_hard);
|
||||
i2c_master_enable(sel_hard, (I2C_BUS_RESET | dev_flags));
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
//TODO: Add the ability to queue messages (adding a boolean to end of function
|
||||
// call, allows for the Arduino style to stay while also giving the flexibility
|
||||
// to bulk send
|
||||
uint8 TwoWire::requestFrom(uint8_t address, uint8_t num_bytes,
|
||||
uint32_t iaddress, uint8_t isize, uint8_t sendStop) {
|
||||
|
||||
ASSERT(master);
|
||||
|
||||
allocateRxBuffer(num_bytes);
|
||||
// error if no memory block available to allocate the buffer
|
||||
if (rxBuffer == nullptr) {
|
||||
return EDATA;
|
||||
}
|
||||
|
||||
// reset tx buffer iterator vars
|
||||
rxBufferIndex = 0;
|
||||
rxBufferLength = 0;
|
||||
|
||||
itc_msg.addr = address;
|
||||
itc_msg.flags = I2C_MSG_READ;
|
||||
itc_msg.length = num_bytes;
|
||||
itc_msg.data = &rxBuffer[rxBufferIndex];
|
||||
process(sendStop); // TODO deal with to the return value
|
||||
|
||||
// TODO handle iaddress & isize
|
||||
rxBufferLength += itc_msg.xferred;
|
||||
itc_msg.flags = 0;
|
||||
|
||||
return rxBufferLength;
|
||||
}
|
||||
|
||||
uint8_t TwoWire::requestFrom(uint8_t address, uint8_t quantity,
|
||||
uint8_t sendStop) {
|
||||
// TODO shouldn't this set flag |= I2C_MSG_10BIT_ADDR ???
|
||||
return requestFrom((uint8_t) address, (uint8_t) quantity, (uint32_t) 0,
|
||||
(uint8_t) 0, sendStop);
|
||||
}
|
||||
|
||||
uint8_t TwoWire::requestFrom(uint8_t address, uint8_t quantity) {
|
||||
return requestFrom((uint8_t) address, (uint8_t) quantity, (uint32_t) 0,
|
||||
(uint8_t) 0, (uint8_t) true);
|
||||
}
|
||||
|
||||
uint8_t TwoWire::requestFrom(int address, int quantity) {
|
||||
return requestFrom((uint8_t) address, (uint8_t) quantity, (uint32_t) 0,
|
||||
(uint8_t) 0, (uint8_t) true);
|
||||
}
|
||||
|
||||
uint8_t TwoWire::requestFrom(int address, int quantity, int sendStop) {
|
||||
return requestFrom((uint8_t) address, (uint8_t) quantity, (uint32_t) 0,
|
||||
(uint8_t) 0, (bool) sendStop);
|
||||
}
|
||||
|
||||
void TwoWire::beginTransmission(uint8_t address) {
|
||||
|
||||
// indicate that we are transmitting
|
||||
transmitting = true;
|
||||
|
||||
// reset tx buffer iterator vars
|
||||
txBufferIndex = 0;
|
||||
txBufferLength = 0;
|
||||
|
||||
itc_msg.addr = address;
|
||||
itc_msg.data = &txBuffer[txBufferIndex];
|
||||
itc_msg.length = 0;
|
||||
itc_msg.flags = 0;
|
||||
|
||||
}
|
||||
|
||||
void TwoWire::beginTransmission(int address) {
|
||||
beginTransmission((uint8_t) address);
|
||||
}
|
||||
|
||||
//
|
||||
// Originally, 'endTransmission' was an f(void) function.
|
||||
// It has been modified to take one parameter indicating
|
||||
// whether or not a STOP should be performed on the bus.
|
||||
// Calling endTransmission(false) allows a sketch to
|
||||
// perform a repeated start.
|
||||
//
|
||||
// WARNING: Nothing in the library keeps track of whether
|
||||
// the bus tenure has been properly ended with a STOP. It
|
||||
// is very possible to leave the bus in a hung state if
|
||||
// no call to endTransmission(true) is made. Some I2C
|
||||
// devices will behave oddly if they do not see a STOP.
|
||||
//
|
||||
uint8_t TwoWire::endTransmission(uint8_t sendStop) {
|
||||
//UNUSED(sendStop);
|
||||
int8_t ret = 4;
|
||||
|
||||
if (master == true) {
|
||||
|
||||
itc_msg.data = txBuffer;
|
||||
itc_msg.length = txBufferLength;
|
||||
itc_msg.flags = 0;
|
||||
ret = process(sendStop); // Changed so that the return value from process is returned by this function see also the return line below
|
||||
txBufferIndex = 0;
|
||||
|
||||
// reset Tx buffer
|
||||
resetTxBuffer(); // TODO why? isn't this just unesssesary?
|
||||
|
||||
// reset tx buffer iterator vars
|
||||
txBufferIndex = 0;
|
||||
txBufferLength = 0;
|
||||
|
||||
// indicate that we are done transmitting
|
||||
transmitting = false;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// This provides backwards compatibility with the original
|
||||
// definition, and expected behaviour, of endTransmission
|
||||
//
|
||||
|
||||
uint8_t TwoWire::endTransmission(void) {
|
||||
return endTransmission(true);
|
||||
}
|
||||
|
||||
// must be called in:
|
||||
// slave tx event callback
|
||||
// or after beginTransmission(address)
|
||||
size_t TwoWire::write(uint8_t data) {
|
||||
if (!transmitting && master) {
|
||||
return 0;
|
||||
} else {
|
||||
// in master transmitter mode or slave tx event callback
|
||||
allocateTxBuffer(txBufferLength + 1);
|
||||
// error if no memory block available to allocate the buffer
|
||||
if (txBuffer == nullptr) {
|
||||
setWriteError();
|
||||
return 0;
|
||||
}
|
||||
// put byte in tx buffer
|
||||
txBuffer[txBufferIndex] = data;
|
||||
++txBufferIndex;
|
||||
// update amount in buffer
|
||||
txBufferLength = txBufferIndex;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief This function must be called in slave Tx event callback or after
|
||||
* beginTransmission() and before endTransmission().
|
||||
* @param pdata: pointer to the buffer data
|
||||
* @param quantity: number of bytes to write
|
||||
* @retval number of bytes ready to write.
|
||||
*/
|
||||
size_t TwoWire::write(const uint8_t *data, size_t quantity) {
|
||||
if (!transmitting && master) {
|
||||
return 0;
|
||||
} else {
|
||||
// in master transmitter mode or slave Tx event callback
|
||||
allocateTxBuffer(txBufferLength + quantity);
|
||||
// error if no memory block available to allocate the buffer
|
||||
if (txBuffer == nullptr) {
|
||||
setWriteError();
|
||||
return 0;
|
||||
}
|
||||
// put bytes in tx buffer
|
||||
memcpy(&(txBuffer[txBufferIndex]), data, quantity);
|
||||
txBufferIndex = txBufferIndex + quantity;
|
||||
// update amount in buffer
|
||||
txBufferLength = txBufferIndex;
|
||||
return quantity;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// must be called in:
|
||||
// slave rx event callback
|
||||
// or after requestFrom(address, numBytes)
|
||||
int TwoWire::available(void) {
|
||||
return rxBufferLength - rxBufferIndex;
|
||||
}
|
||||
|
||||
// must be called in:
|
||||
// slave rx event callback
|
||||
// or after requestFrom(address, numBytes)
|
||||
int TwoWire::read(void) {
|
||||
int value = -1;
|
||||
|
||||
// get each successive byte on each call
|
||||
if (rxBufferIndex < rxBufferLength) {
|
||||
value = rxBuffer[rxBufferIndex];
|
||||
++rxBufferIndex;
|
||||
|
||||
/* Commented as not I think it is not useful
|
||||
* but kept to show that it is possible to
|
||||
* reset rx buffer when no more data available */
|
||||
/*if(rxBufferIndex == rxBufferLength) {
|
||||
resetRxBuffer();
|
||||
}*/
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
// must be called in:
|
||||
// slave rx event callback
|
||||
// or after requestFrom(address, numBytes)
|
||||
int TwoWire::peek(void) {
|
||||
int value = -1;
|
||||
|
||||
if (rxBufferIndex < rxBufferLength) {
|
||||
value = rxBuffer[rxBufferIndex];
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
void TwoWire::flush(void) {
|
||||
rxBufferIndex = 0;
|
||||
rxBufferLength = 0;
|
||||
resetRxBuffer();
|
||||
txBufferIndex = 0;
|
||||
txBufferLength = 0;
|
||||
resetTxBuffer();
|
||||
}
|
||||
|
||||
// behind the scenes function that is called when data is received
|
||||
void __attribute__((always_inline)) TwoWire::onReceiveService(i2c_msg* msg) {
|
||||
// don't bother if user hasn't registered a callback
|
||||
if (!user_onReceive) {
|
||||
return;
|
||||
}
|
||||
|
||||
// don't bother if rx buffer is in use by a master requestFrom() op
|
||||
// i know this drops data, but it allows for slight stupidity
|
||||
// meaning, they may not have read all the master requestFrom() data yet
|
||||
if (rxBufferIndex < rxBufferLength) {
|
||||
return;
|
||||
}
|
||||
// copy twi rx buffer into local read buffer
|
||||
// this enables new reads to happen in parallel
|
||||
//
|
||||
// TODO: Something is strange here, isn't msg->data==rxBuffer? nope, itsnot
|
||||
//
|
||||
memcpy(rxBuffer, msg->data, msg->length);
|
||||
// set rx iterator vars
|
||||
rxBufferIndex = 0;
|
||||
rxBufferLength = msg->length;
|
||||
// alert user program
|
||||
user_onReceive(msg->length);
|
||||
}
|
||||
|
||||
// behind the scenes function that is called when data is requested
|
||||
void __attribute__((always_inline)) TwoWire::onRequestService(i2c_msg* msg) {
|
||||
// don't bother if user hasn't registered a callback
|
||||
if (!user_onRequest) {
|
||||
return;
|
||||
}
|
||||
|
||||
// reset tx buffer iterator vars
|
||||
// !!! this will kill any pending pre-master sendTo() activity
|
||||
txBufferIndex = 0;
|
||||
txBufferLength = 0;
|
||||
// alert user program
|
||||
user_onRequest();
|
||||
|
||||
// update i2c_msg
|
||||
msg->data = txBuffer;
|
||||
msg->length = txBufferLength;
|
||||
msg->xferred = 0;
|
||||
}
|
||||
|
||||
// sets function called on slave write
|
||||
void TwoWire::onReceive(void (*function)(int)) {
|
||||
user_onReceive = function;
|
||||
}
|
||||
|
||||
// sets function called on slave read
|
||||
void TwoWire::onRequest(void (*function)(void)) {
|
||||
user_onRequest = function;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Allocate the Rx/Tx buffer to the requested length if needed
|
||||
* @note Minimum allocated size is BUFFER_LENGTH)
|
||||
* @param length: number of bytes to allocate
|
||||
*/
|
||||
inline void TwoWire::allocateRxBuffer(size_t length) {
|
||||
// By default we allocate BUFFER_LENGTH bytes. It is the min size of the buffer.
|
||||
if (length < BUFFER_LENGTH) {
|
||||
length = BUFFER_LENGTH;
|
||||
}
|
||||
if (rxBufferAllocated < length) {
|
||||
rxBuffer = (uint8_t *) realloc(rxBuffer, length * sizeof(uint8_t));
|
||||
rxBufferAllocated = (rxBuffer != nullptr) ? length : 0;
|
||||
}
|
||||
}
|
||||
|
||||
inline void TwoWire::allocateTxBuffer(size_t length) {
|
||||
// By default we allocate BUFFER_LENGTH bytes. It is the min size of the buffer.
|
||||
if (length < BUFFER_LENGTH) {
|
||||
length = BUFFER_LENGTH;
|
||||
}
|
||||
if (txBufferAllocated < length) {
|
||||
txBuffer = (uint8_t *) realloc(txBuffer, length * sizeof(uint8_t));
|
||||
txBufferAllocated = (txBuffer != nullptr) ? length : 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Reset Rx/Tx buffer content to 0
|
||||
*/
|
||||
inline void TwoWire::resetRxBuffer(void) {
|
||||
if (rxBuffer != nullptr)
|
||||
memset(rxBuffer, 0, rxBufferAllocated);
|
||||
}
|
||||
|
||||
inline void TwoWire::resetTxBuffer(void) {
|
||||
if (txBuffer != nullptr)
|
||||
memset(txBuffer, 0, txBufferAllocated);
|
||||
}
|
||||
|
||||
|
||||
// Preinstantiate Objects //////////////////////////////////////////////////////
|
||||
TwoWire& Wire = TwoWire::getInstance(); //SCL:D14 SDA:D15
|
||||
#if WIRE_INTERFACES_COUNT > 1
|
||||
TwoWire& Wire1 = TwoWire::getInstance1(); //SCL: D1 SDA: D0
|
||||
#endif
|
||||
|
||||
// Static methods //////////////////////////////////////////////////////////////
|
||||
|
||||
TwoWire& TwoWire::getInstance(){
|
||||
static TwoWire* instance = nullptr;
|
||||
if (!instance) {
|
||||
instance = new TwoWire(I2C1);
|
||||
}
|
||||
return *instance;
|
||||
}
|
||||
|
||||
#if WIRE_INTERFACES_COUNT > 1
|
||||
TwoWire& TwoWire::getInstance1(){
|
||||
static TwoWire* instance = nullptr;
|
||||
if (!instance) {
|
||||
instance = new TwoWire(I2C2);
|
||||
}
|
||||
return *instance;
|
||||
}
|
||||
#endif
|
||||
|
||||
// onRequestServiceX and onReceiveServiceX can't be inline since they
|
||||
// are exclusively called via a function pointer
|
||||
void TwoWire::onRequestService1(i2c_msg* msg) {
|
||||
Wire.onRequestService(msg);
|
||||
}
|
||||
|
||||
void TwoWire::onReceiveService1(i2c_msg* msg) {
|
||||
Wire.onReceiveService(msg);
|
||||
}
|
||||
|
||||
#if WIRE_INTERFACES_COUNT > 1
|
||||
void TwoWire::onRequestService2(i2c_msg* msg) {
|
||||
Wire1.onRequestService(msg);
|
||||
}
|
||||
void TwoWire::onReceiveService2(i2c_msg* msg) {
|
||||
Wire1.onReceiveService(msg);
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,151 @@
|
|||
/*
|
||||
TwoWire.h - TWI/I2C library for Arduino & Wiring
|
||||
Copyright (c) 2006 Nicholas Zambetti. All right 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
|
||||
|
||||
Modified 2012 by Todd Krein (todd@krein.org) to implement repeated starts
|
||||
*/
|
||||
|
||||
#ifndef TwoWire_h
|
||||
#define TwoWire_h
|
||||
|
||||
#include <inttypes.h>
|
||||
#include "Stream.h"
|
||||
#include "Arduino.h"
|
||||
#include <libmaple/i2c_slave.h>
|
||||
|
||||
|
||||
// WIRE_HAS_END means Wire has end()
|
||||
#ifndef WIRE_HAS_END
|
||||
#define WIRE_HAS_END 1
|
||||
#endif
|
||||
|
||||
#ifndef WIRE_INTERFACES_COUNT
|
||||
#define WIRE_INTERFACES_COUNT 2
|
||||
#endif
|
||||
|
||||
/* return codes from endTransmission() */
|
||||
typedef enum EndTranmissionCodes {
|
||||
SUCCESS = 0, /* transmission was successful */
|
||||
EDATA = 1, /* too much data */
|
||||
ENACKADDR = 2, /* received nack on transmit of address */
|
||||
ENACKTRNS = 3, /* received nack on transmit of data */
|
||||
EOTHER = 4, /* other error */
|
||||
} EndTranmissionCodes;
|
||||
|
||||
class TwoWire: public Stream {
|
||||
private:
|
||||
i2c_dev* sel_hard;
|
||||
|
||||
uint8_t *rxBuffer; // lazy allocation
|
||||
uint8_t rxBufferAllocated;
|
||||
uint8_t rxBufferIndex;
|
||||
uint8_t rxBufferLength;
|
||||
|
||||
uint8_t *txBuffer; // lazy allocation
|
||||
uint8_t txBufferAllocated;
|
||||
uint8_t txBufferIndex;
|
||||
uint8_t txBufferLength;
|
||||
|
||||
bool transmitting;
|
||||
|
||||
bool master;
|
||||
|
||||
uint8 dev_flags;
|
||||
i2c_msg itc_msg;
|
||||
i2c_msg itc_slave_msg;
|
||||
|
||||
void (*user_onRequest)(void);
|
||||
void (*user_onReceive)(int quantity);
|
||||
|
||||
void allocateRxBuffer(size_t length);
|
||||
void allocateTxBuffer(size_t length);
|
||||
|
||||
void resetRxBuffer(void);
|
||||
void resetTxBuffer(void);
|
||||
uint8 process(bool stop = true); // wrapper for i2c_master_xfer
|
||||
|
||||
inline void __attribute__((always_inline)) onReceiveService(i2c_msg* msg);
|
||||
inline void __attribute__((always_inline)) onRequestService(i2c_msg* msg);
|
||||
|
||||
static void onRequestService1(i2c_msg*);
|
||||
static void onReceiveService1(i2c_msg*);
|
||||
#if WIRE_INTERFACES_COUNT > 1
|
||||
static void onRequestService2(i2c_msg*);
|
||||
static void onReceiveService2(i2c_msg*);
|
||||
#endif
|
||||
|
||||
TwoWire(i2c_dev* i2cDevice);
|
||||
TwoWire() = delete;
|
||||
TwoWire(const TwoWire&) = delete;
|
||||
TwoWire& operator=(const TwoWire&) = delete;
|
||||
TwoWire(TwoWire&&) = delete;
|
||||
TwoWire& operator=(TwoWire&&) = delete;
|
||||
|
||||
public:
|
||||
// return the 'Wire' instance (using I2C1)
|
||||
static TwoWire& getInstance();
|
||||
#if WIRE_INTERFACES_COUNT > 1
|
||||
// return the 'Wire1' instance (using I2C2)
|
||||
static TwoWire& getInstance1();
|
||||
#endif
|
||||
|
||||
void begin(); // master mode
|
||||
void begin(uint8_t myAddress); //slave mode
|
||||
void begin(int myAddress); //slave mode
|
||||
void end();
|
||||
void setClock(uint32_t frequencyHz);
|
||||
void beginTransmission(uint8_t slaveAddress);
|
||||
void beginTransmission(int slaveAddress);
|
||||
uint8_t endTransmission(void);
|
||||
uint8_t endTransmission(uint8_t sendStop);
|
||||
|
||||
uint8_t requestFrom(uint8_t slaveAddress, uint8_t quantity);
|
||||
uint8_t requestFrom(uint8_t slaveAddress, uint8_t quantity, uint8_t sendStop);
|
||||
uint8_t requestFrom(uint8_t slaveAddress, uint8_t num_bytes, uint32_t iaddress, uint8_t isize, uint8_t sendStop);
|
||||
uint8_t requestFrom(int slaveAddress, int quantity);
|
||||
uint8_t requestFrom(int slaveAddress, int quantity, int sendStop);
|
||||
|
||||
virtual size_t write(uint8_t data);
|
||||
virtual size_t write(const uint8_t *data, size_t quantity);
|
||||
virtual int available(void);
|
||||
virtual int read(void);
|
||||
virtual int peek(void);
|
||||
virtual void flush(void);
|
||||
void onReceive(void (*)(int quantity));
|
||||
void onRequest(void (*)(void));
|
||||
|
||||
inline size_t write(unsigned long data) {
|
||||
return write((uint8_t) data);
|
||||
}
|
||||
inline size_t write(long data) {
|
||||
return write((uint8_t) data);
|
||||
}
|
||||
inline size_t write(unsigned int data) {
|
||||
return write((uint8_t) data);
|
||||
}
|
||||
inline size_t write(int data) {
|
||||
return write((uint8_t) data);
|
||||
}
|
||||
using Print::write;
|
||||
};
|
||||
|
||||
extern TwoWire& Wire;
|
||||
#if WIRE_INTERFACES_COUNT > 1
|
||||
extern TwoWire& Wire1;
|
||||
#endif
|
||||
|
||||
#endif // TwoWire_h
|
Binary file not shown.
|
@ -0,0 +1,795 @@
|
|||
/******************************************************************************
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2010 Perry Hung.
|
||||
* Copyright (c) 2012 LeafLabs, LLC.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use, copy,
|
||||
* modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* @file libmaple/i2c.c
|
||||
* @author Perry Hung <perry@leaflabs.com>
|
||||
* @author Barry Carter <barry.carter@gmail.com>
|
||||
* @brief Inter-Integrated Circuit (I2C) support.
|
||||
*
|
||||
* Master and Slave supported
|
||||
* I2C slave support added 2012 by Barry Carter. barry.carter@gmail.com, headfuzz.co.uk
|
||||
*/
|
||||
|
||||
#include "libmaple/i2c_common_slave.h"
|
||||
|
||||
#include "i2c_private.h"
|
||||
|
||||
|
||||
#include "libmaple/i2c_slave.h"
|
||||
#include <libmaple/libmaple.h>
|
||||
#include <libmaple/rcc.h>
|
||||
#include <libmaple/gpio.h>
|
||||
#include <libmaple/nvic.h>
|
||||
#include <libmaple/systick.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
static inline int32 wait_for_state_change(i2c_dev *dev,
|
||||
i2c_state state,
|
||||
uint32 timeout);
|
||||
static void set_ccr_trise(i2c_dev *dev, uint32 flags);
|
||||
|
||||
/**
|
||||
* @brief Fill data register with slave address
|
||||
* @param dev I2C device
|
||||
* @param addr Slave address
|
||||
* @param rw Read/write bit
|
||||
*/
|
||||
static inline void i2c_send_slave_addr(i2c_dev *dev, uint32 addr, uint32 rw) {
|
||||
dev->regs->DR = (addr << 1) | rw;
|
||||
}
|
||||
|
||||
/*
|
||||
* Simple debugging trail. Define I2C_DEBUG to turn on.
|
||||
*/
|
||||
#ifdef I2C_DEBUG
|
||||
|
||||
#define NR_CRUMBS 128
|
||||
struct crumb {
|
||||
uint32 event;
|
||||
uint32 arg0;
|
||||
uint32 arg1;
|
||||
uint32 arg2; // filler to make the data fit GDB memory dump screen
|
||||
};
|
||||
static struct crumb crumbs[NR_CRUMBS];
|
||||
static uint32 cur_crumb = 0;
|
||||
|
||||
static inline void i2c_drop_crumb(uint32 event, uint32 arg0, uint32 arg1) {
|
||||
if (cur_crumb < NR_CRUMBS) {
|
||||
struct crumb *crumb = &crumbs[cur_crumb++];
|
||||
crumb->event = event;
|
||||
crumb->arg0 = arg0;
|
||||
crumb->arg1 = arg1;
|
||||
crumb->arg2 = cur_crumb-1;
|
||||
}
|
||||
}
|
||||
#define I2C_CRUMB(event, arg0, arg1) i2c_drop_crumb(event, arg0, arg1)
|
||||
|
||||
#else
|
||||
#define I2C_CRUMB(event, arg0, arg1)
|
||||
#endif
|
||||
|
||||
enum {
|
||||
IRQ_ENTRY = 1,
|
||||
TXE_ONLY = 2,
|
||||
TXE_BTF = 3,
|
||||
STOP_SENT = 4,
|
||||
TEST = 5,
|
||||
RX_ADDR_START = 6,
|
||||
RX_ADDR_STOP = 7,
|
||||
RXNE_ONLY = 8,
|
||||
RXNE_SENDING = 9,
|
||||
RXNE_START_SENT = 10,
|
||||
RXNE_STOP_SENT = 11,
|
||||
RXNE_DONE = 12,
|
||||
ERROR_ENTRY = 13,
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Reset an I2C bus.
|
||||
*
|
||||
* Reset is accomplished by clocking out pulses until any hung slaves
|
||||
* release SDA and SCL, then generating a START condition, then a STOP
|
||||
* condition.
|
||||
*
|
||||
* @param dev I2C device
|
||||
*/
|
||||
void i2c_bus_reset(const i2c_dev *dev) {
|
||||
/* Release both lines */
|
||||
i2c_master_release_bus(dev);
|
||||
|
||||
/*
|
||||
* Make sure the bus is free by clocking it until any slaves release the
|
||||
* bus.
|
||||
*/
|
||||
while (!gpio_read_bit(sda_port(dev), dev->sda_pin)) {
|
||||
/* Wait for any clock stretching to finish */
|
||||
while (!gpio_read_bit(scl_port(dev), dev->scl_pin))
|
||||
;
|
||||
delay_us(10);
|
||||
|
||||
/* Pull low */
|
||||
gpio_write_bit(scl_port(dev), dev->scl_pin, 0);
|
||||
delay_us(10);
|
||||
|
||||
/* Release high again */
|
||||
gpio_write_bit(scl_port(dev), dev->scl_pin, 1);
|
||||
delay_us(10);
|
||||
}
|
||||
|
||||
/* Generate start then stop condition */
|
||||
gpio_write_bit(sda_port(dev), dev->sda_pin, 0);
|
||||
delay_us(10);
|
||||
gpio_write_bit(scl_port(dev), dev->scl_pin, 0);
|
||||
delay_us(10);
|
||||
gpio_write_bit(scl_port(dev), dev->scl_pin, 1);
|
||||
delay_us(10);
|
||||
gpio_write_bit(sda_port(dev), dev->sda_pin, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initialize an I2C device and reset its registers to their
|
||||
* default values.
|
||||
* @param dev Device to initialize.
|
||||
*/
|
||||
void i2c_init(i2c_dev *dev) {
|
||||
|
||||
rcc_reset_dev(dev->clk_id);
|
||||
rcc_clk_enable(dev->clk_id);
|
||||
_i2c_irq_priority_fixup(dev);
|
||||
}
|
||||
|
||||
/* Hack for deprecated bit of STM32F1 functionality */
|
||||
#ifndef _I2C_HAVE_DEPRECATED_I2C_REMAP
|
||||
#define _i2c_handle_remap(dev, flags) ((void)0)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Initialize an I2C device as bus master
|
||||
* @param dev Device to enable
|
||||
* @param flags Bitwise or of the following I2C options:
|
||||
* I2C_FAST_MODE: 400 khz operation,
|
||||
* I2C_DUTY_16_9: 16/9 Tlow/Thigh duty cycle (only applicable for
|
||||
* fast mode),
|
||||
* I2C_BUS_RESET: Reset the bus and clock out any hung slaves on
|
||||
* initialization,
|
||||
* I2C_10BIT_ADDRESSING: Enable 10-bit addressing,
|
||||
* I2C_REMAP: (deprecated, STM32F1 only) Remap I2C1 to SCL/PB8
|
||||
* SDA/PB9.
|
||||
*/
|
||||
void i2c_master_enable(i2c_dev *dev, uint32 flags) {
|
||||
/* PE must be disabled to configure the device */
|
||||
ASSERT(!(dev->regs->CR1 & I2C_CR1_PE));
|
||||
|
||||
/* Ugh */
|
||||
_i2c_handle_remap(dev, flags);
|
||||
|
||||
/* Reset the bus. Clock out any hung slaves. */
|
||||
if (flags & I2C_BUS_RESET) {
|
||||
i2c_bus_reset(dev);
|
||||
}
|
||||
|
||||
/* Turn on clock and set GPIO modes */
|
||||
i2c_init(dev);
|
||||
i2c_config_gpios(dev);
|
||||
|
||||
/* Configure clock and rise time, but only if in master mode */
|
||||
if (!(flags & (I2C_SLAVE_DUAL_ADDRESS|I2C_SLAVE_GENERAL_CALL))) {
|
||||
set_ccr_trise(dev, flags);
|
||||
}
|
||||
|
||||
/* Enable event and buffer interrupts */
|
||||
nvic_irq_enable(dev->ev_nvic_line);
|
||||
nvic_irq_enable(dev->er_nvic_line);
|
||||
i2c_enable_irq(dev, I2C_IRQ_EVENT | I2C_IRQ_BUFFER | I2C_IRQ_ERROR);
|
||||
|
||||
/* Configure the slave unit */
|
||||
if (flags & I2C_SLAVE_DUAL_ADDRESS) {
|
||||
i2c_slave_dual_address_enable(dev);
|
||||
}
|
||||
|
||||
if (flags & I2C_SLAVE_GENERAL_CALL) {
|
||||
i2c_slave_general_call_enable(dev);
|
||||
}
|
||||
|
||||
/* store all of the flags */
|
||||
dev->config_flags = flags;
|
||||
|
||||
/* Make it go! */
|
||||
i2c_peripheral_enable(dev);
|
||||
i2c_enable_ack(dev);
|
||||
|
||||
dev->state = I2C_STATE_IDLE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initialize an I2C device as slave (and master)
|
||||
* @param dev Device to enable
|
||||
* @param flags Bitwise or of the following I2C options:
|
||||
* I2C_FAST_MODE: 400 khz operation,
|
||||
* I2C_DUTY_16_9: 16/9 Tlow/Thigh duty cycle (only applicable for
|
||||
* fast mode),
|
||||
* I2C_BUS_RESET: Reset the bus and clock out any hung slaves on
|
||||
* initialization,
|
||||
* I2C_10BIT_ADDRESSING: Enable 10-bit addressing,
|
||||
* I2C_REMAP: (deprecated, STM32F1 only) Remap I2C1 to SCL/PB8
|
||||
* SDA/PB9.
|
||||
* I2C_SLAVE_DUAL_ADDRESS: Slave can respond on 2 i2C addresses
|
||||
* I2C_SLAVE_GENERAL_CALL: SLA+W broadcast to all general call
|
||||
* listeners on bus. Addr 0x00
|
||||
* I2C_SLAVE_USE_RX_BUFFER: Use a buffer to receive the incoming
|
||||
* data. Callback at end of recv
|
||||
* I2C_SLAVE_USE_TX_BUFFER: Use a buffer to transmit data.
|
||||
* Callback will be called before tx
|
||||
*/
|
||||
void i2c_slave_enable(i2c_dev *dev, uint32 flags) {
|
||||
// TODO: Figure out why i2c_disable(I2C2) causes a crash when I2C1 is enabled
|
||||
i2c_disable(dev);
|
||||
i2c_master_enable(dev, flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Process an i2c transaction.
|
||||
*
|
||||
* Transactions are composed of one or more i2c_msg's, and may be read
|
||||
* or write tranfers. Multiple i2c_msg's will generate a repeated
|
||||
* start in between messages.
|
||||
*
|
||||
* @param dev I2C device
|
||||
* @param msgs Messages to send/receive
|
||||
* @param num Number of messages to send/receive
|
||||
* @param timeout Bus idle timeout in milliseconds before aborting the
|
||||
* transfer. 0 denotes no timeout.
|
||||
* @return 0 on success,
|
||||
* I2C_ERROR_PROTOCOL if there was a protocol error,
|
||||
* I2C_ERROR_TIMEOUT if the transfer timed out.
|
||||
*/
|
||||
int32 i2c_master_xfer(i2c_dev *dev,
|
||||
i2c_msg *msgs,
|
||||
uint16 num,
|
||||
uint32 timeout) {
|
||||
int32 rc;
|
||||
|
||||
ASSERT(dev->state == I2C_STATE_IDLE);
|
||||
|
||||
dev->msg = msgs;
|
||||
dev->msgs_left = num;
|
||||
dev->timestamp = systick_uptime();
|
||||
dev->state = I2C_STATE_BUSY;
|
||||
|
||||
i2c_enable_irq(dev, I2C_IRQ_EVENT);
|
||||
i2c_start_condition(dev);
|
||||
|
||||
rc = wait_for_state_change(dev, I2C_STATE_XFER_DONE, timeout);
|
||||
if (rc < 0) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
dev->state = I2C_STATE_IDLE;
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Wait for an I2C event, or time out in case of error.
|
||||
* @param dev I2C device
|
||||
* @param state I2C_state state to wait for
|
||||
* @param timeout Timeout, in milliseconds
|
||||
* @return 0 if target state is reached, a negative value on error.
|
||||
*/
|
||||
static inline int32 wait_for_state_change(i2c_dev *dev,
|
||||
i2c_state state,
|
||||
uint32 timeout) {
|
||||
i2c_state tmp;
|
||||
|
||||
while (1) {
|
||||
tmp = dev->state;
|
||||
|
||||
if (tmp == I2C_STATE_ERROR) {
|
||||
return I2C_STATE_ERROR;
|
||||
}
|
||||
|
||||
if (tmp == state) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (timeout) {
|
||||
if (systick_uptime() > (dev->timestamp + timeout)) {
|
||||
/* TODO: overflow? */
|
||||
/* TODO: racy? */
|
||||
return I2C_ERROR_TIMEOUT;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Private API
|
||||
*/
|
||||
|
||||
/*
|
||||
* IRQ handler for I2C master and slave.
|
||||
* Handles transmission/reception.
|
||||
*/
|
||||
void _i2c_irq_handler(i2c_dev *dev) {
|
||||
/* WTFs:
|
||||
* - Where is I2C_MSG_10BIT_ADDR handled?
|
||||
*/
|
||||
i2c_msg *msg = dev->msg;
|
||||
|
||||
uint8 read = msg->flags & I2C_MSG_READ;
|
||||
|
||||
uint32 sr1 = dev->regs->SR1;
|
||||
uint32 sr2 = dev->regs->SR2;
|
||||
I2C_CRUMB(IRQ_ENTRY, sr1, sr2);
|
||||
|
||||
/*
|
||||
* Reset timeout counter
|
||||
*/
|
||||
dev->timestamp = systick_uptime();
|
||||
|
||||
/*
|
||||
* Add Slave support
|
||||
*/
|
||||
|
||||
/* Check to see if in slave mode */
|
||||
if (dev->config_flags&(I2C_SLAVE_DUAL_ADDRESS|I2C_SLAVE_GENERAL_CALL)) {
|
||||
|
||||
/* Check for address match */
|
||||
if (sr1 & I2C_SR1_ADDR) {
|
||||
/* Find out which address was matched */
|
||||
/* Check the general call address first */
|
||||
if (sr2 & I2C_SR2_GENCALL) {
|
||||
dev->i2c_slave_msg->addr = 0;
|
||||
}
|
||||
/* We matched the secondary address */
|
||||
else if (sr2 & I2C_SR2_DUALF) {
|
||||
dev->i2c_slave_msg->addr = dev->regs->OAR2 & 0xFE;
|
||||
}
|
||||
/* We matched the primary address */
|
||||
else if ((sr2 & I2C_SR2_DUALF) != I2C_SR2_DUALF) {
|
||||
dev->i2c_slave_msg->addr = dev->regs->OAR1 & 0xFE;
|
||||
}
|
||||
/* Shouldn't get here */
|
||||
else {
|
||||
dev->i2c_slave_msg->addr = -1; /* uh oh */
|
||||
}
|
||||
|
||||
/* if we have buffered io */
|
||||
if ((dev->config_flags & I2C_SLAVE_USE_RX_BUFFER) ||
|
||||
(dev->config_flags & I2C_SLAVE_USE_TX_BUFFER)) {
|
||||
|
||||
/* if receiving then this would be a repeated start
|
||||
*
|
||||
*if we have some bytes already
|
||||
*/
|
||||
if ((dev->state == I2C_STATE_SL_RX) &&
|
||||
(dev->i2c_slave_msg->xferred > 0) &&
|
||||
(dev->config_flags & I2C_SLAVE_USE_RX_BUFFER)) {
|
||||
/* Call the callback with the contents of the data */
|
||||
if (dev->i2c_slave_recv_callback != NULL) {
|
||||
(*(dev->i2c_slave_recv_callback))(dev->i2c_slave_msg);
|
||||
}
|
||||
}
|
||||
|
||||
/* Reset the message back to defaults.
|
||||
* We are starting a new message
|
||||
*/
|
||||
dev->i2c_slave_msg->flags = 0;
|
||||
dev->i2c_slave_msg->length = 0;
|
||||
dev->i2c_slave_msg->xferred = 0;
|
||||
dev->msgs_left = 0;
|
||||
dev->timestamp = systick_uptime();
|
||||
|
||||
/* We have been addressed with SLA+R so
|
||||
* the master wants us to transmit
|
||||
*/
|
||||
if ((sr1 & I2C_SR1_TXE) &&
|
||||
(dev->config_flags & I2C_SLAVE_USE_TX_BUFFER)) {
|
||||
/* Call the transmit callback so it can populate the msg
|
||||
* data with the bytes to go
|
||||
*/
|
||||
if (dev->i2c_slave_transmit_callback != NULL) {
|
||||
(*(dev->i2c_slave_transmit_callback))(dev->i2c_slave_msg);
|
||||
}
|
||||
}
|
||||
dev->state = I2C_STATE_BUSY;
|
||||
}
|
||||
|
||||
sr1 = sr2 = 0;
|
||||
}
|
||||
|
||||
/* EV3: Master requesting data from slave. Transmit a byte*/
|
||||
if (sr1 & I2C_SR1_TXE) {
|
||||
if (dev->config_flags & I2C_SLAVE_USE_TX_BUFFER) {
|
||||
if (dev->i2c_slave_msg->xferred >= dev->i2c_slave_msg->length) {
|
||||
/* End of the transmit buffer? If so we NACK */
|
||||
i2c_disable_ack(dev);
|
||||
/* We have to either issue a STOP or write something here.
|
||||
* STOP here seems to screw up some masters,
|
||||
* For now padding with 0
|
||||
*/
|
||||
i2c_write(dev, 0);
|
||||
/*i2c_stop_condition(dev); // This is causing bus lockups way more than it should !? Seems some I2C master devices freak out here*/
|
||||
}
|
||||
else
|
||||
{
|
||||
/* NACk the last byte */
|
||||
if (dev->i2c_slave_msg->xferred == dev->i2c_slave_msg->length-1) {
|
||||
i2c_disable_ack(dev);
|
||||
}
|
||||
else {
|
||||
i2c_enable_ack(dev);
|
||||
}
|
||||
i2c_write(dev, dev->i2c_slave_msg->data[dev->i2c_slave_msg->xferred++]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Call the callback to get the data we need.
|
||||
* The callback is expected to write using i2c_write(...)
|
||||
* If the slave is going to terminate the transfer, this function should
|
||||
* also do a NACK on the last byte!
|
||||
*/
|
||||
if (dev->i2c_slave_transmit_callback != NULL) (*(dev->i2c_slave_transmit_callback))(dev->i2c_slave_msg);
|
||||
}
|
||||
|
||||
dev->state = I2C_STATE_BUSY;
|
||||
sr1 = sr2 = 0;
|
||||
}
|
||||
|
||||
/* EV2: Slave received data from a master. Get from DR */
|
||||
if (sr1 & I2C_SR1_RXNE) {
|
||||
if (dev->config_flags & I2C_SLAVE_USE_RX_BUFFER) {
|
||||
/* Fill the buffer with the contents of the data register */
|
||||
/* These is potential for buffer overflow here, so we should
|
||||
* really store the size of the array. This is expensive in
|
||||
* the ISR so left out for now. We must trust the implementor!
|
||||
*/
|
||||
dev->i2c_slave_msg->data[dev->i2c_slave_msg->xferred++] = dev->regs->DR;
|
||||
dev->i2c_slave_msg->length++;
|
||||
}
|
||||
else {
|
||||
/* Call the callback with the contents of the data */
|
||||
dev->i2c_slave_msg->data[0] = dev->regs->DR;
|
||||
if (dev->i2c_slave_recv_callback != NULL) (*(dev->i2c_slave_recv_callback))(dev->i2c_slave_msg);
|
||||
}
|
||||
dev->state = I2C_STATE_SL_RX;
|
||||
sr1 = sr2 = 0;
|
||||
}
|
||||
|
||||
/* EV4: Slave has detected a STOP condition on the bus */
|
||||
if (sr1 & I2C_SR1_STOPF) {
|
||||
dev->regs->CR1 |= I2C_CR1_PE;
|
||||
|
||||
if ((dev->config_flags & I2C_SLAVE_USE_RX_BUFFER) ||
|
||||
(dev->config_flags & I2C_SLAVE_USE_TX_BUFFER)) {
|
||||
|
||||
/* The callback with the data will happen on a NACK of the last data byte.
|
||||
* This is handled in the error IRQ (AF bit)
|
||||
*/
|
||||
/* Handle the case where the master misbehaves by sending no NACK */
|
||||
if (dev->state != I2C_STATE_IDLE) {
|
||||
if (dev->state == I2C_STATE_SL_RX) {
|
||||
if (dev->i2c_slave_recv_callback != NULL) (*(dev->i2c_slave_recv_callback))(dev->i2c_slave_msg);
|
||||
}
|
||||
else {
|
||||
if (dev->i2c_slave_transmit_callback != NULL) (*(dev->i2c_slave_transmit_callback))(dev->i2c_slave_msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sr1 = sr2 = 0;
|
||||
dev->state = I2C_STATE_IDLE;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* EV5: Start condition sent
|
||||
*/
|
||||
if (sr1 & I2C_SR1_SB) {
|
||||
msg->xferred = 0;
|
||||
i2c_enable_irq(dev, I2C_IRQ_BUFFER);
|
||||
|
||||
/*
|
||||
* Master receiver
|
||||
*/
|
||||
if (read) {
|
||||
i2c_enable_ack(dev);
|
||||
}
|
||||
|
||||
i2c_send_slave_addr(dev, msg->addr, read);
|
||||
sr1 = sr2 = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* EV6: Slave address sent
|
||||
*/
|
||||
if (sr1 & I2C_SR1_ADDR) {
|
||||
/*
|
||||
* Special case event EV6_1 for master receiver.
|
||||
* Generate NACK and restart/stop condition after ADDR
|
||||
* is cleared.
|
||||
*/
|
||||
if (read) {
|
||||
if (msg->length == 1) {
|
||||
i2c_disable_ack(dev);
|
||||
if (dev->msgs_left > 1) {
|
||||
i2c_start_condition(dev);
|
||||
I2C_CRUMB(RX_ADDR_START, 0, 0);
|
||||
} else {
|
||||
i2c_stop_condition(dev);
|
||||
I2C_CRUMB(RX_ADDR_STOP, 0, 0);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* Master transmitter: write first byte to fill shift
|
||||
* register. We should get another TXE interrupt
|
||||
* immediately to fill DR again.
|
||||
*/
|
||||
if (msg->length > 1) {
|
||||
i2c_write(dev, msg->data[msg->xferred++]);
|
||||
} else if (msg->length == 0) { /* We're just sending an address */
|
||||
i2c_stop_condition(dev);
|
||||
/*
|
||||
* Turn off event interrupts to keep BTF from firing until
|
||||
* the end of the stop condition. Why on earth they didn't
|
||||
* have a start/stop condition request clear BTF is beyond
|
||||
* me.
|
||||
*/
|
||||
i2c_disable_irq(dev, I2C_IRQ_EVENT);
|
||||
I2C_CRUMB(STOP_SENT, 0, 0);
|
||||
dev->state = I2C_STATE_XFER_DONE;
|
||||
} /* else we're just sending one byte */
|
||||
}
|
||||
sr1 = sr2 = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* EV8: Master transmitter
|
||||
* Transmit buffer empty, but we haven't finished transmitting the last
|
||||
* byte written.
|
||||
*/
|
||||
if ((sr1 & I2C_SR1_TXE) && !(sr1 & I2C_SR1_BTF)) {
|
||||
I2C_CRUMB(TXE_ONLY, 0, 0);
|
||||
if (dev->msgs_left) {
|
||||
i2c_write(dev, msg->data[msg->xferred++]);
|
||||
if (msg->xferred == msg->length) {
|
||||
/*
|
||||
* End of this message. Turn off TXE/RXNE and wait for
|
||||
* BTF to send repeated start or stop condition.
|
||||
*/
|
||||
i2c_disable_irq(dev, I2C_IRQ_BUFFER);
|
||||
dev->msgs_left--;
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* This should be impossible...
|
||||
*/
|
||||
ASSERT(0);
|
||||
}
|
||||
sr1 = sr2 = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* EV8_2: Master transmitter
|
||||
* Last byte sent, program repeated start/stop
|
||||
*/
|
||||
if ((sr1 & I2C_SR1_TXE) && (sr1 & I2C_SR1_BTF)) {
|
||||
I2C_CRUMB(TXE_BTF, 0, 0);
|
||||
if (dev->msgs_left) {
|
||||
I2C_CRUMB(TEST, 0, 0);
|
||||
/*
|
||||
* Repeated start insanity: We can't disable ITEVTEN or else SB
|
||||
* won't interrupt, but if we don't disable ITEVTEN, BTF will
|
||||
* continually interrupt us. What the fuck ST?
|
||||
*/
|
||||
i2c_start_condition(dev);
|
||||
while (!(dev->regs->SR1 & I2C_SR1_SB))
|
||||
;
|
||||
dev->msg++;
|
||||
} else {
|
||||
i2c_stop_condition(dev);
|
||||
|
||||
/*
|
||||
* Turn off event interrupts to keep BTF from firing until
|
||||
* the end of the stop condition. Why on earth they didn't
|
||||
* have a start/stop condition request clear BTF is beyond
|
||||
* me.
|
||||
*/
|
||||
i2c_disable_irq(dev, I2C_IRQ_EVENT);
|
||||
I2C_CRUMB(STOP_SENT, 0, 0);
|
||||
dev->state = I2C_STATE_XFER_DONE;
|
||||
}
|
||||
sr1 = sr2 = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* EV7: Master Receiver
|
||||
*/
|
||||
if (sr1 & I2C_SR1_RXNE) {
|
||||
I2C_CRUMB(RXNE_ONLY, 0, 0);
|
||||
msg->data[msg->xferred++] = dev->regs->DR;
|
||||
|
||||
/*
|
||||
* EV7_1: Second to last byte in the reception? Set NACK and generate
|
||||
* stop/restart condition in time for the last byte. We'll get one more
|
||||
* RXNE interrupt before shutting things down.
|
||||
*/
|
||||
if (msg->xferred == (msg->length - 1)) {
|
||||
i2c_disable_ack(dev);
|
||||
if (dev->msgs_left > 2) {
|
||||
i2c_start_condition(dev);
|
||||
I2C_CRUMB(RXNE_START_SENT, 0, 0);
|
||||
} else {
|
||||
i2c_stop_condition(dev);
|
||||
I2C_CRUMB(RXNE_STOP_SENT, 0, 0);
|
||||
}
|
||||
} else if (msg->xferred == msg->length) {
|
||||
dev->msgs_left--;
|
||||
if (dev->msgs_left == 0) {
|
||||
/*
|
||||
* We're done.
|
||||
*/
|
||||
I2C_CRUMB(RXNE_DONE, 0, 0);
|
||||
dev->state = I2C_STATE_XFER_DONE;
|
||||
} else {
|
||||
dev->msg++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Interrupt handler for I2C error conditions. Aborts any pending I2C
|
||||
* transactions.
|
||||
*/
|
||||
void _i2c_irq_error_handler(i2c_dev *dev) {
|
||||
I2C_CRUMB(ERROR_ENTRY, dev->regs->SR1, dev->regs->SR2);
|
||||
|
||||
dev->error_flags = dev->regs->SR1 & (I2C_SR1_BERR |
|
||||
I2C_SR1_ARLO |
|
||||
I2C_SR1_AF |
|
||||
I2C_SR1_OVR);
|
||||
|
||||
/* Are we in slave mode? */
|
||||
if (dev->config_flags & (I2C_SLAVE_DUAL_ADDRESS|I2C_SLAVE_GENERAL_CALL)) {
|
||||
/* Check to see if the master device did a NAK on the last bit
|
||||
* This is perfectly valid for a master to do this on the bus.
|
||||
* We ignore this. Any further error processing takes us into dead
|
||||
* loop waiting for the stop condition that will never arrive
|
||||
*/
|
||||
if (dev->regs->SR1 & I2C_SR1_AF) {
|
||||
/* Clear flags */
|
||||
dev->regs->SR1 = 0;
|
||||
dev->regs->SR2 = 0;
|
||||
/* We need to write something to CR1 to clear the flag.
|
||||
* This isn't really mentioned but seems important */
|
||||
i2c_enable_ack(dev);
|
||||
|
||||
if (dev->state == I2C_STATE_SL_RX &&
|
||||
dev->config_flags & I2C_SLAVE_USE_RX_BUFFER &&
|
||||
dev->i2c_slave_msg->xferred > 0) {
|
||||
/* Call the callback with the contents of the data */
|
||||
if (dev->i2c_slave_recv_callback != NULL) (*(dev->i2c_slave_recv_callback))(dev->i2c_slave_msg);
|
||||
}
|
||||
|
||||
dev->state = I2C_STATE_IDLE;
|
||||
return;
|
||||
}
|
||||
/* Catch any other strange errors while in slave mode.
|
||||
* I have seen BERR caused by an over fast master device
|
||||
* as well as several overflows and arbitration failures.
|
||||
* We are going to reset SR flags and carry on at this point which
|
||||
* is not the best thing to do, but stops the bus locking up completely
|
||||
* If we carry on below and send the stop bit, the code spins forever */
|
||||
/* Clear flags */
|
||||
dev->regs->SR1 = 0;
|
||||
dev->regs->SR2 = 0;
|
||||
dev->state = I2C_STATE_IDLE;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Clear flags */
|
||||
dev->regs->SR1 = 0;
|
||||
dev->regs->SR2 = 0;
|
||||
|
||||
i2c_stop_condition(dev);
|
||||
i2c_disable_irq(dev, I2C_IRQ_BUFFER | I2C_IRQ_EVENT | I2C_IRQ_ERROR);
|
||||
dev->state = I2C_STATE_ERROR;
|
||||
}
|
||||
|
||||
/*
|
||||
* CCR/TRISE configuration helper
|
||||
*/
|
||||
static void set_ccr_trise(i2c_dev *dev, uint32 flags) {
|
||||
uint32 ccr = 0;
|
||||
uint32 trise = 0;
|
||||
uint32 clk_mhz = _i2c_bus_clk(dev);
|
||||
uint32 clk_hz = clk_mhz * (1000 * 1000);
|
||||
|
||||
i2c_set_input_clk(dev, clk_mhz);
|
||||
|
||||
if (flags & I2C_FAST_MODE) {
|
||||
ccr |= I2C_CCR_FS;
|
||||
|
||||
if (flags & I2C_DUTY_16_9) {
|
||||
/* Tlow/Thigh = 16/9 */
|
||||
ccr |= I2C_CCR_DUTY_16_9;
|
||||
ccr |= clk_hz / (400000 * 25);
|
||||
} else {
|
||||
/* Tlow/Thigh = 2 */
|
||||
ccr |= clk_hz / (400000 * 3);
|
||||
}
|
||||
|
||||
trise = (300 * clk_mhz / 1000) + 1;
|
||||
} else {
|
||||
/* Tlow/Thigh = 1 */
|
||||
ccr = clk_hz / (100000 * 2);
|
||||
trise = clk_mhz + 1;
|
||||
}
|
||||
|
||||
/* Set minimum required value if CCR < 1*/
|
||||
if ((ccr & I2C_CCR_CCR) == 0) {
|
||||
ccr |= 0x1;
|
||||
}
|
||||
|
||||
i2c_set_clk_control(dev, ccr);
|
||||
i2c_set_trise(dev, trise);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief callback for when the device acts as a slave. If using an rx buffer, this is triggered
|
||||
* after the last byte, otherwise it is called for every incoming packet.
|
||||
* @param dev I2C device
|
||||
* @param msg The dev_msg to pass to the slave init code
|
||||
* @param func The function pointer to call
|
||||
*/
|
||||
void i2c_slave_attach_recv_handler(i2c_dev *dev, i2c_msg *msg, i2c_slave_recv_callback_func func) {
|
||||
dev->i2c_slave_recv_callback = func;
|
||||
dev->i2c_slave_msg = msg;
|
||||
msg->xferred = 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief callback for when the device acts as a slave. If using a tx buffer, this is triggered
|
||||
* after the device is successsfully addressed with SLA+R.
|
||||
* @param dev I2C device
|
||||
* @param msg The dev_msg to pass to the slave init code
|
||||
* @param func The function pointer to call
|
||||
*/
|
||||
void i2c_slave_attach_transmit_handler(i2c_dev *dev, i2c_msg *msg, i2c_slave_transmit_callback_func func) {
|
||||
dev->i2c_slave_transmit_callback = func;
|
||||
dev->i2c_slave_msg = msg;
|
||||
msg->xferred = 0;
|
||||
}
|
|
@ -0,0 +1,110 @@
|
|||
/******************************************************************************
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2010 Perry Hung (from <libmaple/i2c.h>).
|
||||
* Copyright (c) 2012 LeafLabs, LLC.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use, copy,
|
||||
* modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* @file libmaple/include/libmaple/i2c_common.h
|
||||
* @author Marti Bolivar <mbolivar@leaflabs.com>
|
||||
* @brief This file is an implementation detail
|
||||
*
|
||||
* CONTENTS UNSTABLE. The existence of this file is an implementation
|
||||
* detail. Never include it directly. If you need something from
|
||||
* here, include <libmaple/i2c.h> instead.
|
||||
*
|
||||
* I2C slave support added 2012 by Barry Carter. barry.carter@gmail.com, headfuzz.co.uk
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _LIBMAPLE_I2C_COMMON_H_
|
||||
#define _LIBMAPLE_I2C_COMMON_H_
|
||||
|
||||
#include <libmaple/libmaple_types.h>
|
||||
#include <libmaple/nvic.h>
|
||||
#include <libmaple/rcc.h>
|
||||
|
||||
struct gpio_dev;
|
||||
struct i2c_reg_map;
|
||||
struct i2c_msg;
|
||||
|
||||
/** I2C device states */
|
||||
typedef enum i2c_state {
|
||||
I2C_STATE_DISABLED = 0, /**< Disabled */
|
||||
I2C_STATE_IDLE = 1, /**< Idle */
|
||||
I2C_STATE_XFER_DONE = 2, /**< Done with transfer */
|
||||
I2C_STATE_BUSY = 3, /**< Busy */
|
||||
I2C_STATE_SL_RX = 4, /**< Slave receiving */
|
||||
I2C_STATE_ERROR = -1 /**< Error occurred */
|
||||
} i2c_state;
|
||||
|
||||
typedef void (*i2c_slave_recv_callback_func)(struct i2c_msg *);
|
||||
typedef void (*i2c_slave_transmit_callback_func)(struct i2c_msg *);
|
||||
/**
|
||||
* @brief I2C device type.
|
||||
*/
|
||||
typedef struct i2c_dev {
|
||||
struct i2c_reg_map *regs; /**< Register map */
|
||||
struct i2c_msg *msg; /**< Messages */
|
||||
uint32 error_flags; /**< Error flags, set on I2C error condition */
|
||||
volatile uint32 timestamp; /**< For internal use */
|
||||
|
||||
/**
|
||||
* @brief Deprecated. Use .scl_port or .sda_port instead.
|
||||
* If non-null, this will be used as SDA, SCL pins' GPIO port. If
|
||||
* null, then .sda_port will be used for SDA, and .sda_port for
|
||||
* SDA. */
|
||||
struct gpio_dev *gpio_port;
|
||||
|
||||
/**
|
||||
* @brief SDA GPIO device (but see .gpio_port).
|
||||
*/
|
||||
struct gpio_dev *sda_port;
|
||||
|
||||
/**
|
||||
* @brief SCL GPIO device (but see .gpio_port).
|
||||
*/
|
||||
struct gpio_dev *scl_port;
|
||||
|
||||
uint16 msgs_left; /**< Messages left */
|
||||
uint8 sda_pin; /**< SDA bit on gpio_port */
|
||||
uint8 scl_pin; /**< SCL bit on gpio_port */
|
||||
rcc_clk_id clk_id; /**< RCC clock information */
|
||||
nvic_irq_num ev_nvic_line; /**< Event IRQ number */
|
||||
nvic_irq_num er_nvic_line; /**< Error IRQ number */
|
||||
volatile i2c_state state; /**< Device state */
|
||||
uint32 config_flags; /**< Configuration flags */
|
||||
|
||||
/*
|
||||
* Slave implementation. Callback functions in this struct allow
|
||||
* for a separate callback function for each I2C unit available onboard
|
||||
*/
|
||||
i2c_slave_transmit_callback_func i2c_slave_transmit_callback;
|
||||
i2c_slave_recv_callback_func i2c_slave_recv_callback;
|
||||
|
||||
struct i2c_msg *i2c_slave_msg; /* the message that the i2c slave will use */
|
||||
|
||||
} i2c_dev;
|
||||
|
||||
#endif
|
|
@ -0,0 +1,475 @@
|
|||
/******************************************************************************
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2010 Perry Hung.
|
||||
* Copyright (c) 2012 LeafLabs, LLC.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use, copy,
|
||||
* modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* @file libmaple/include/libmaple/i2c.h
|
||||
* @brief Inter-Integrated Circuit (I2C) peripheral support
|
||||
*
|
||||
* Supports Master and Slave.
|
||||
* Master Usage notes:
|
||||
*
|
||||
* - Enable an I2C device with i2c_master_enable().
|
||||
* - Initialize an array of struct i2c_msg to suit the bus
|
||||
* transactions (reads/writes) you wish to perform.
|
||||
* - Call i2c_master_xfer() to do the work.
|
||||
*
|
||||
* Slave Usage notes:
|
||||
* - Enable I2C slave by calling i2c_slave_enable().
|
||||
* Check flags for usage. Enabling master also enabled slave.
|
||||
* - initialise the i2c_msg struct and the data buffer
|
||||
* - initialise the callback functions
|
||||
*
|
||||
* I2C slave support added 2012 by Barry Carter. barry.carter@gmail.com, headfuzz.co.uk
|
||||
*/
|
||||
|
||||
#ifndef _LIBMAPLE_I2C_H_
|
||||
#define _LIBMAPLE_I2C_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Series header must provide:
|
||||
*
|
||||
* - uint32 _i2c_bus_clk(i2c_dev*): Clock frequency of dev's bus, in
|
||||
* MHz. (This is for internal use only).
|
||||
*
|
||||
* - (optional) _I2C_HAVE_IRQ_FIXUP: Leave undefined, or define to 1.
|
||||
* This is for internal use only. It's a hack to work around a
|
||||
* silicon bug related to I2C IRQ pre-emption on some targets. If 1,
|
||||
* the series header must also declare and implement a routine with
|
||||
* this signature (it may also be provided as a macro):
|
||||
*
|
||||
* void _i2c_irq_priority_fixup(i2c_dev*)
|
||||
*
|
||||
* This will be called by i2c_enable_irq() before actually enabling
|
||||
* I2C interrupts.
|
||||
*
|
||||
* - Reg. map base pointers, device pointer declarations.
|
||||
*/
|
||||
|
||||
/* Roger clark. Replaced with line below #include <series/i2c.h>*/
|
||||
#include "libmaple/i2c_common_slave.h"
|
||||
#include "stm32f1/include/series/i2c.h"
|
||||
|
||||
#include <libmaple/libmaple_types.h>
|
||||
#include <libmaple/rcc.h>
|
||||
#include <libmaple/nvic.h>
|
||||
#include <libmaple/gpio.h>
|
||||
|
||||
/** I2C register map type */
|
||||
typedef struct i2c_reg_map {
|
||||
__io uint32 CR1; /**< Control register 1 */
|
||||
__io uint32 CR2; /**< Control register 2 */
|
||||
__io uint32 OAR1; /**< Own address register 1 */
|
||||
__io uint32 OAR2; /**< Own address register 2 */
|
||||
__io uint32 DR; /**< Data register */
|
||||
__io uint32 SR1; /**< Status register 1 */
|
||||
__io uint32 SR2; /**< Status register 2 */
|
||||
__io uint32 CCR; /**< Clock control register */
|
||||
__io uint32 TRISE; /**< TRISE (rise time) register */
|
||||
} i2c_reg_map;
|
||||
|
||||
/**
|
||||
* @brief I2C message type
|
||||
*/
|
||||
typedef struct i2c_msg {
|
||||
uint16 addr; /**< Address */
|
||||
|
||||
#define I2C_MSG_READ 0x1
|
||||
#define I2C_MSG_10BIT_ADDR 0x2
|
||||
/**
|
||||
* Bitwise OR of:
|
||||
* - I2C_MSG_READ (write is default)
|
||||
* - I2C_MSG_10BIT_ADDR (7-bit is default) */
|
||||
uint16 flags;
|
||||
|
||||
uint16 length; /**< Message length */
|
||||
uint16 xferred; /**< Messages transferred */
|
||||
uint8 *data; /**< Data */
|
||||
} i2c_msg;
|
||||
|
||||
/*
|
||||
* Register bit definitions
|
||||
*/
|
||||
|
||||
/* Control register 1 */
|
||||
|
||||
#define I2C_CR1_SWRST (1U << 15) // Software reset
|
||||
#define I2C_CR1_ALERT (1U << 13) // SMBus alert
|
||||
#define I2C_CR1_PEC (1U << 12) // Packet error checking
|
||||
#define I2C_CR1_POS (1U << 11) // Acknowledge/PEC position
|
||||
#define I2C_CR1_ACK (1U << 10) // Acknowledge enable
|
||||
#define I2C_CR1_STOP (1U << 9) // Stop generation
|
||||
#define I2C_CR1_START (1U << 8) // Start generation
|
||||
#define I2C_CR1_NOSTRETCH (1U << 7) // Clock stretching disable
|
||||
#define I2C_CR1_ENGC (1U << 6) // General call enable
|
||||
#define I2C_CR1_ENPEC (1U << 5) // PEC enable
|
||||
#define I2C_CR1_ENARP (1U << 4) // ARP enable
|
||||
#define I2C_CR1_SMBTYPE (1U << 3) // SMBus type
|
||||
#define I2C_CR1_SMBTYPE_DEVICE (0U << 3) // SMBus type: device
|
||||
#define I2C_CR1_SMBTYPE_HOST (1U << 3) // SMBus type: host
|
||||
#define I2C_CR1_SMBUS (1U << 1) // SMBus mode
|
||||
#define I2C_CR1_SMBUS_I2C (0U << 1) // SMBus mode: I2C
|
||||
#define I2C_CR1_SMBUS_SMBUS (1U << 1) // SMBus mode: SMBus
|
||||
#define I2C_CR1_PE (1U << 0) // Peripheral Enable
|
||||
|
||||
/* Control register 2 */
|
||||
|
||||
#define I2C_CR2_LAST (1U << 12) // DMA last transfer
|
||||
#define I2C_CR2_DMAEN (1U << 11) // DMA requests enable
|
||||
#define I2C_CR2_ITBUFEN (1U << 10) // Buffer interrupt enable
|
||||
#define I2C_CR2_ITEVTEN (1U << 9) // Event interupt enable
|
||||
#define I2C_CR2_ITERREN (1U << 8) // Error interupt enable
|
||||
#define I2C_CR2_FREQ 0x3F // Peripheral input frequency
|
||||
|
||||
/* Own address register 1 */
|
||||
|
||||
#define I2C_OAR1_ADDMODE (1U << 15) // Addressing mode
|
||||
#define I2C_OAR1_ADDMODE_7_BIT (0U << 15) // Addressing mode: 7-bit
|
||||
#define I2C_OAR1_ADDMODE_10_BIT (1U << 15) // Addressing mode: 10-bit
|
||||
#define I2C_OAR1_ADD 0x3FF // Interface address
|
||||
|
||||
/* Own address register 2 */
|
||||
|
||||
#define I2C_OAR2_ADD2 0xFE // Interface address
|
||||
#define I2C_OAR2_ENDUAL 1U // Dual addressing mode enable
|
||||
|
||||
/* Status register 1 */
|
||||
|
||||
#define I2C_SR1_SMBALERT (1U << 15) // SMBus alert
|
||||
#define I2C_SR1_TIMEOUT (1U << 14) // Timeout or Tlow error
|
||||
#define I2C_SR1_PECERR (1U << 12) // PEC Error in reception
|
||||
#define I2C_SR1_OVR (1U << 11) // Overrun/underrun
|
||||
#define I2C_SR1_AF (1U << 10) // Acknowledge failure
|
||||
#define I2C_SR1_ARLO (1U << 9) // Arbitration lost
|
||||
#define I2C_SR1_BERR (1U << 8) // Bus error
|
||||
#define I2C_SR1_TXE (1U << 7) // Data register empty
|
||||
#define I2C_SR1_RXNE (1U << 6) // Data register not empty
|
||||
#define I2C_SR1_STOPF (1U << 4) // Stop detection
|
||||
#define I2C_SR1_ADD10 (1U << 3) // 10-bit header sent
|
||||
#define I2C_SR1_BTF (1U << 2) // Byte transfer finished
|
||||
#define I2C_SR1_ADDR (1U << 1) // Address sent/matched
|
||||
#define I2C_SR1_SB (1U << 0) // Start bit
|
||||
|
||||
/* Status register 2 */
|
||||
|
||||
#define I2C_SR2_PEC 0xFF00 // Packet error checking register
|
||||
#define I2C_SR2_DUALF (1U << 7) // Dual flag
|
||||
#define I2C_SR2_SMBHOST (1U << 6) // SMBus host header
|
||||
#define I2C_SR2_SMBDEFAULT (1U << 5) // SMBus device default address
|
||||
#define I2C_SR2_GENCALL (1U << 4) // General call address
|
||||
#define I2C_SR2_TRA (1U << 2) // Transmitter/receiver
|
||||
#define I2C_SR2_BUSY (1U << 1) // Bus busy
|
||||
#define I2C_SR2_MSL (1U << 0) // Master/slave
|
||||
|
||||
/* Clock control register */
|
||||
|
||||
#define I2C_CCR_FS (1U << 15) // Fast mode selection
|
||||
#define I2C_CCR_DUTY (1U << 14) // Fast mode duty cycle
|
||||
#define I2C_CCR_DUTY_2_1 (0U << 14) // Fast mode duty: 2/1
|
||||
#define I2C_CCR_DUTY_16_9 (1U << 14) // Fast mode duty: 16/9
|
||||
#define I2C_CCR_CCR 0xFFF // Clock control bits
|
||||
|
||||
/*
|
||||
* Convenience routines
|
||||
*/
|
||||
|
||||
/* Main I2C API */
|
||||
|
||||
/* I2C enable options */
|
||||
#define I2C_FAST_MODE 0x1 // 400 khz
|
||||
#define I2C_DUTY_16_9 0x2 // 16/9 duty ratio
|
||||
/* Flag 0x4 is reserved; DO NOT USE. */
|
||||
#define I2C_BUS_RESET 0x8 // Perform a bus reset
|
||||
#define I2C_SLAVE_USE_RX_BUFFER 0x10 // Use a buffered message when doing a slave recv
|
||||
#define I2C_SLAVE_USE_TX_BUFFER 0x20 // Use a buffered message when doing a slave transmit
|
||||
#define I2C_SLAVE_DUAL_ADDRESS 0x40 // Enable the dual slave address scheme
|
||||
#define I2C_SLAVE_GENERAL_CALL 0x80 // Enable the general call on address 0x00
|
||||
void i2c_master_enable(i2c_dev *dev, uint32 flags);
|
||||
void i2c_slave_enable(i2c_dev *dev, uint32 flags);
|
||||
|
||||
#define I2C_ERROR_PROTOCOL (-1)
|
||||
#define I2C_ERROR_TIMEOUT (-2)
|
||||
int32 i2c_master_xfer(i2c_dev *dev, i2c_msg *msgs, uint16 num, uint32 timeout);
|
||||
|
||||
void i2c_bus_reset(const i2c_dev *dev);
|
||||
|
||||
/**
|
||||
* @brief Disable an I2C device
|
||||
*
|
||||
* This function disables the corresponding peripheral and marks dev's
|
||||
* state as I2C_STATE_DISABLED.
|
||||
*
|
||||
* @param dev Device to disable.
|
||||
*/
|
||||
static inline void i2c_disable(i2c_dev *dev) {
|
||||
dev->regs->CR1 &= ~I2C_CR1_PE;
|
||||
dev->state = I2C_STATE_DISABLED;
|
||||
}
|
||||
|
||||
/* Start/stop conditions */
|
||||
|
||||
/**
|
||||
* @brief Generate a start condition on the bus.
|
||||
* @param dev I2C device
|
||||
*/
|
||||
static inline void i2c_start_condition(i2c_dev *dev) {
|
||||
uint32 cr1;
|
||||
while ((cr1 = dev->regs->CR1) & (I2C_CR1_START |
|
||||
I2C_CR1_STOP |
|
||||
I2C_CR1_PEC)) {
|
||||
;
|
||||
}
|
||||
dev->regs->CR1 |= I2C_CR1_START;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Generate a stop condition on the bus
|
||||
* @param dev I2C device
|
||||
*/
|
||||
static inline void i2c_stop_condition(i2c_dev *dev) {
|
||||
uint32 cr1;
|
||||
while ((cr1 = dev->regs->CR1) & (I2C_CR1_START |
|
||||
I2C_CR1_STOP |
|
||||
I2C_CR1_PEC)) {
|
||||
;
|
||||
}
|
||||
dev->regs->CR1 |= I2C_CR1_STOP;
|
||||
while ((cr1 = dev->regs->CR1) & (I2C_CR1_START |
|
||||
I2C_CR1_STOP |
|
||||
I2C_CR1_PEC)) {
|
||||
;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* IRQ enable/disable */
|
||||
|
||||
#ifndef _I2C_HAVE_IRQ_FIXUP
|
||||
/* The series header provides this if _I2C_HAVE_IRQ_FIXUP is defined,
|
||||
* but we need it either way. */
|
||||
#define _i2c_irq_priority_fixup(dev) ((void)0)
|
||||
#endif
|
||||
|
||||
#define I2C_IRQ_ERROR I2C_CR2_ITERREN
|
||||
#define I2C_IRQ_EVENT I2C_CR2_ITEVTEN
|
||||
#define I2C_IRQ_BUFFER I2C_CR2_ITBUFEN
|
||||
/**
|
||||
* @brief Enable one or more I2C interrupts
|
||||
* @param dev I2C device
|
||||
* @param irqs Bitwise or of:
|
||||
* I2C_IRQ_ERROR (error interrupt),
|
||||
* I2C_IRQ_EVENT (event interrupt), and
|
||||
* I2C_IRQ_BUFFER (buffer interrupt).
|
||||
*/
|
||||
static inline void i2c_enable_irq(i2c_dev *dev, uint32 irqs) {
|
||||
_i2c_irq_priority_fixup(dev);
|
||||
dev->regs->CR2 |= irqs;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Disable one or more I2C interrupts
|
||||
* @param dev I2C device
|
||||
* @param irqs Bitwise or of:
|
||||
* I2C_IRQ_ERROR (error interrupt),
|
||||
* I2C_IRQ_EVENT (event interrupt), and
|
||||
* I2C_IRQ_BUFFER (buffer interrupt).
|
||||
*/
|
||||
static inline void i2c_disable_irq(i2c_dev *dev, uint32 irqs) {
|
||||
dev->regs->CR2 &= ~irqs;
|
||||
}
|
||||
|
||||
/* ACK/NACK */
|
||||
|
||||
/**
|
||||
* @brief Enable I2C acknowledgment
|
||||
* @param dev I2C device
|
||||
*/
|
||||
static inline void i2c_enable_ack(i2c_dev *dev) {
|
||||
dev->regs->CR1 |= I2C_CR1_ACK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Disable I2C acknowledgment
|
||||
* @param dev I2C device
|
||||
*/
|
||||
static inline void i2c_disable_ack(i2c_dev *dev) {
|
||||
dev->regs->CR1 &= ~I2C_CR1_ACK;
|
||||
}
|
||||
|
||||
/* GPIO control */
|
||||
|
||||
/**
|
||||
* @brief Configure device GPIOs.
|
||||
*
|
||||
* Configure GPIO bits dev->sda_pin and dev->scl_pin on GPIO device
|
||||
* dev->gpio_port for use with I2C device dev.
|
||||
*
|
||||
* @param dev I2C Device
|
||||
* @see i2c_release_gpios()
|
||||
*/
|
||||
extern void i2c_config_gpios(const i2c_dev *dev);
|
||||
|
||||
/**
|
||||
* @brief Release GPIOs controlling an I2C bus
|
||||
*
|
||||
* Releases the I2C bus controlled by dev as master, and disconnects
|
||||
* GPIO bits dev->sda_pin and dev->scl_pin on GPIO device
|
||||
* dev->gpio_port from I2C device dev.
|
||||
*
|
||||
* @param dev I2C device
|
||||
* @see i2c_config_gpios()
|
||||
*/
|
||||
extern void i2c_master_release_bus(const i2c_dev *dev);
|
||||
|
||||
/* Miscellaneous low-level routines */
|
||||
|
||||
void i2c_init(i2c_dev *dev);
|
||||
|
||||
/**
|
||||
* @brief Turn on an I2C peripheral
|
||||
* @param dev Device to enable
|
||||
*/
|
||||
static inline void i2c_peripheral_enable(i2c_dev *dev) {
|
||||
dev->regs->CR1 |= I2C_CR1_PE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Turn off an I2C peripheral
|
||||
* @param dev Device to turn off
|
||||
*/
|
||||
static inline void i2c_peripheral_disable(i2c_dev *dev) {
|
||||
dev->regs->CR1 &= ~I2C_CR1_PE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Fill transmit register
|
||||
* @param dev I2C device
|
||||
* @param byte Byte to write
|
||||
*/
|
||||
static inline void i2c_write(i2c_dev *dev, uint8 byte) {
|
||||
dev->regs->DR = byte;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set input clock frequency, in MHz
|
||||
* @param dev I2C device
|
||||
* @param freq Frequency, in MHz. This must be at least 2, and at most
|
||||
* the APB frequency of dev's bus. (For example, if
|
||||
* rcc_dev_clk(dev) == RCC_APB1, freq must be at most
|
||||
* PCLK1, in MHz). There is an additional limit of 46 MHz.
|
||||
*/
|
||||
static inline void i2c_set_input_clk(i2c_dev *dev, uint32 freq) {
|
||||
#define I2C_MAX_FREQ_MHZ 46
|
||||
ASSERT(2 <= freq && freq <= _i2c_bus_clk(dev) && freq <= I2C_MAX_FREQ_MHZ);
|
||||
uint32 cr2 = dev->regs->CR2;
|
||||
cr2 &= ~I2C_CR2_FREQ;
|
||||
cr2 |= freq;
|
||||
dev->regs->CR2 = freq;
|
||||
#undef I2C_MAX_FREQ_MHZ
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set I2C clock control register.
|
||||
*
|
||||
* See the chip reference manual for the details.
|
||||
*
|
||||
* @param dev I2C device
|
||||
* @param val Value to use for clock control register (in
|
||||
* Fast/Standard mode)
|
||||
*/
|
||||
static inline void i2c_set_clk_control(i2c_dev *dev, uint32 val) {
|
||||
uint32 ccr = dev->regs->CCR;
|
||||
ccr &= ~I2C_CCR_CCR;
|
||||
ccr |= val;
|
||||
dev->regs->CCR = ccr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set SCL rise time
|
||||
* @param dev I2C device
|
||||
* @param trise Maximum rise time in fast/standard mode (see chip
|
||||
* reference manual for the relevant formulas).
|
||||
*/
|
||||
static inline void i2c_set_trise(i2c_dev *dev, uint32 trise) {
|
||||
dev->regs->TRISE = trise;
|
||||
}
|
||||
|
||||
/*
|
||||
* Slave support
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Enable Dual addressing mode to allow peripheral to have 2 addresses
|
||||
* @param dev I2C device
|
||||
*/
|
||||
static inline void i2c_slave_dual_address_enable(i2c_dev *dev) {
|
||||
dev->regs->OAR2 |= I2C_OAR2_ENDUAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enable General Call to allow the unit to respond on addr 0x00
|
||||
* @param dev I2C device
|
||||
*/
|
||||
static inline void i2c_slave_general_call_enable(i2c_dev *dev) {
|
||||
dev->regs->CR1 |= I2C_CR1_ENGC;
|
||||
}
|
||||
|
||||
/* callback functions */
|
||||
/* Callback handler for data received over the bus */
|
||||
void i2c_slave_attach_recv_handler(i2c_dev *dev, i2c_msg *msg, i2c_slave_recv_callback_func func);
|
||||
|
||||
/* Callback handler for data being requested over the bus
|
||||
* The callback function must call i2c_write to get the data over the bus
|
||||
*/
|
||||
void i2c_slave_attach_transmit_handler(i2c_dev *dev, i2c_msg *msg, i2c_slave_transmit_callback_func func);
|
||||
|
||||
/**
|
||||
* @brief Set the primary I2c slave address
|
||||
* @param dev I2C device
|
||||
* @param address the 8 or 10 bit i2c address
|
||||
*/
|
||||
static inline void i2c_slave_set_own_address(i2c_dev *dev, uint16 address) {
|
||||
dev->regs->OAR1 = address <<1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the secondary I2c slave address
|
||||
* @param dev I2C device
|
||||
* @param address the 8 or 10 bit i2c address
|
||||
*/
|
||||
static inline void i2c_slave_set_own_address2(i2c_dev *dev, uint16 address) {
|
||||
dev->regs->OAR2 = (address <<1 ) | I2C_OAR2_ENDUAL;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue