Merge branch 'WireSlave' of https://github.com/lacklustrlabs/Arduino_STM32 into lacklustrlabs-WireSlave

This commit is contained in:
Roger Clark 2018-01-24 08:05:27 +11:00
commit 9c61fbd094
21 changed files with 2648 additions and 0 deletions

View File

@ -0,0 +1 @@
#error "Something is trying to include Wire_slave.h when Wire.h is already included, they are mutually exclusive"

View File

@ -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

View File

@ -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();
}
*/

View File

@ -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);
}

View File

@ -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();
}
}
}

View File

@ -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
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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
}

View File

@ -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

View File

@ -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
}

View File

@ -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
}

View File

@ -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)
#######################################

View File

@ -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

View File

@ -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"

View File

@ -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

View File

@ -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.

View File

@ -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;
}

View File

@ -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

View File

@ -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