Added Wire_slave.h (no actual i2c slave implementation yet)
This commit is contained in:
parent
9cd2f3a743
commit
0f78c266ed
|
@ -0,0 +1 @@
|
||||||
|
#error "Something is trying to include Wire.h when Wire_Slave.h is already included, they are mutually exclusive"
|
|
@ -0,0 +1,10 @@
|
||||||
|
name=Wire
|
||||||
|
version=1.0
|
||||||
|
author=Arduino
|
||||||
|
maintainer=Arduino <info@arduino.cc>
|
||||||
|
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
|
|
@ -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(9600); // 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,122 @@
|
||||||
|
/*
|
||||||
|
* 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 <Arduino.h>
|
||||||
|
#include <libmaple/i2c.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
|
||||||
|
|
||||||
|
// #define Serial Serial1
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
Serial.begin(115200);
|
||||||
|
while(!Serial)
|
||||||
|
;
|
||||||
|
Serial.println("I2C Slave 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,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(9600); // start serial for output
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop()
|
||||||
|
{
|
||||||
|
Wire.requestFrom(2, 6); // request 6 bytes from slave device #2
|
||||||
|
|
||||||
|
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,38 @@
|
||||||
|
// 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()
|
||||||
|
{
|
||||||
|
Wire.begin(4); // join i2c bus with address #4
|
||||||
|
Wire.onReceive(receiveEvent); // register event
|
||||||
|
Serial.begin(9600); // start serial for output
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop()
|
||||||
|
{
|
||||||
|
delay(100);
|
||||||
|
}
|
||||||
|
|
||||||
|
// function that executes whenever data is received from master
|
||||||
|
// this function is registered as an event, see setup()
|
||||||
|
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(2); // join i2c bus with address #2
|
||||||
|
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,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 @@
|
||||||
|
#error "Something is trying to include Wire_slave.h when Wire.h is already included, they are mutually exclusive"
|
Binary file not shown.
|
@ -0,0 +1,106 @@
|
||||||
|
/******************************************************************************
|
||||||
|
* The MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2010 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 TwoWire.cpp
|
||||||
|
* @author Trystan Jones <crenn6977@gmail.com>
|
||||||
|
* @brief Wire library, uses the hardware I2C available in the Maple to
|
||||||
|
* interact with I2C slave devices.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Library created by crenn to use the new WireBase system and allow Arduino
|
||||||
|
* users easy interaction with the I2C Hardware in a familiar method.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Wire_slave.h"
|
||||||
|
|
||||||
|
uint8 TwoWire::process(uint8 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8 TwoWire::process(){
|
||||||
|
return process(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Add in Error Handling if devsel is out of range for other Maples
|
||||||
|
TwoWire::TwoWire(uint8 dev_sel, uint8 flags) {
|
||||||
|
if (dev_sel == 1) {
|
||||||
|
sel_hard = I2C1;
|
||||||
|
} else if (dev_sel == 2) {
|
||||||
|
sel_hard = I2C2;
|
||||||
|
} else {
|
||||||
|
ASSERT(1);
|
||||||
|
}
|
||||||
|
dev_flags = flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
TwoWire::~TwoWire() {
|
||||||
|
i2c_disable(sel_hard);
|
||||||
|
sel_hard = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TwoWire::begin(uint8 self_addr) {
|
||||||
|
i2c_master_enable(sel_hard, dev_flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TwoWire::end() {
|
||||||
|
i2c_disable(sel_hard);
|
||||||
|
sel_hard = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TwoWire Wire(1);
|
|
@ -0,0 +1,78 @@
|
||||||
|
/******************************************************************************
|
||||||
|
* The MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2010 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 Wire.h
|
||||||
|
* @author Trystan Jones <crenn6977@gmail.com>
|
||||||
|
* @brief Wire library, uses the hardware I2C available in the Maple to
|
||||||
|
* interact with I2C slave devices.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Library created by crenn to use the new WireBase system and allow Arduino
|
||||||
|
* users easy interaction with the I2C Hardware in a familiar method.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _TWOWIRE_H_
|
||||||
|
#define _TWOWIRE_H_
|
||||||
|
|
||||||
|
#include "utility/WireBase_slave.h"
|
||||||
|
#include "wirish.h"
|
||||||
|
#include <libmaple/i2c_slave.h>
|
||||||
|
|
||||||
|
class TwoWire : public WireBase {
|
||||||
|
private:
|
||||||
|
i2c_dev* sel_hard;
|
||||||
|
uint8 dev_flags;
|
||||||
|
protected:
|
||||||
|
/*
|
||||||
|
* Processes the incoming I2C message defined by WireBase to the
|
||||||
|
* hardware. If an error occured, restart the I2C device.
|
||||||
|
*/
|
||||||
|
uint8 process(uint8);
|
||||||
|
uint8 process();
|
||||||
|
public:
|
||||||
|
/*
|
||||||
|
* Check if devsel is within range and enable selected I2C interface with
|
||||||
|
* passed flags
|
||||||
|
*/
|
||||||
|
TwoWire(uint8, uint8 = 0);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Shuts down (disables) the hardware I2C
|
||||||
|
*/
|
||||||
|
void end();
|
||||||
|
|
||||||
|
void setClock(uint32_t frequencyHz);
|
||||||
|
/*
|
||||||
|
* Disables the I2C device and remove the device address.
|
||||||
|
*/
|
||||||
|
~TwoWire();
|
||||||
|
|
||||||
|
void begin(uint8 = 0x00);
|
||||||
|
};
|
||||||
|
extern TwoWire Wire;
|
||||||
|
#endif // _TWOWIRE_H_
|
Binary file not shown.
|
@ -0,0 +1,789 @@
|
||||||
|
/******************************************************************************
|
||||||
|
* 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
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#define I2C_CRUMB(event, arg0, arg1) i2c_drop_crumb(event, arg0, arg1)
|
||||||
|
|
||||||
|
#else
|
||||||
|
#define I2C_CRUMB(event, arg0, arg1)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct crumb {
|
||||||
|
uint32 event;
|
||||||
|
uint32 arg0;
|
||||||
|
uint32 arg1;
|
||||||
|
};
|
||||||
|
|
||||||
|
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 */
|
||||||
|
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) {
|
||||||
|
i2c_disable(dev);
|
||||||
|
i2c_master_enable(dev, dev->config_flags | 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. 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 MSL master slave bit is set */
|
||||||
|
if ((sr2 & I2C_SR2_MSL) != I2C_SR2_MSL) { /* 0 = slave mode 1 = master */
|
||||||
|
|
||||||
|
/* 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->regs->SR2 & I2C_SR2_MSL) != I2C_SR2_MSL) {
|
||||||
|
/* 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;
|
||||||
|
}
|
Binary file not shown.
|
@ -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
|
Binary file not shown.
|
@ -0,0 +1,145 @@
|
||||||
|
/******************************************************************************
|
||||||
|
* The MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2010 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 WireBase.cpp
|
||||||
|
* @author Trystan Jones <crenn6977@gmail.com>
|
||||||
|
* @brief Wire library, following the majority of the interface from Arduino.
|
||||||
|
* Provides a 'standard' interface to I2C (two-wire) communication for
|
||||||
|
* derived classes.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Library created by crenn to allow a system which would provide users the
|
||||||
|
* 'standardised' Arduino method for interfacing with I2C devices regardless of
|
||||||
|
* whether it is I2C hardware or emulating software.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "WireBase_slave.h"
|
||||||
|
#include "wirish.h"
|
||||||
|
|
||||||
|
void WireBase::begin(uint8 self_addr) {
|
||||||
|
tx_buf_idx = 0;
|
||||||
|
tx_buf_overflow = false;
|
||||||
|
rx_buf_idx = 0;
|
||||||
|
rx_buf_len = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WireBase::beginTransmission(uint8 slave_address) {
|
||||||
|
itc_msg.addr = slave_address;
|
||||||
|
itc_msg.data = &tx_buf[tx_buf_idx];
|
||||||
|
itc_msg.length = 0;
|
||||||
|
itc_msg.flags = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WireBase::beginTransmission(int slave_address) {
|
||||||
|
beginTransmission((uint8)slave_address);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8 WireBase::endTransmission(bool stop) {
|
||||||
|
uint8 retVal;
|
||||||
|
if (tx_buf_overflow) {
|
||||||
|
return EDATA;
|
||||||
|
}
|
||||||
|
retVal = process(stop);// Changed so that the return value from process is returned by this function see also the return line below
|
||||||
|
tx_buf_idx = 0;
|
||||||
|
tx_buf_overflow = false;
|
||||||
|
return retVal;//SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8 WireBase::endTransmission(){
|
||||||
|
endTransmission(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
//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 WireBase::requestFrom(uint8 address, int num_bytes) {
|
||||||
|
if (num_bytes > BUFFER_LENGTH) {
|
||||||
|
num_bytes = BUFFER_LENGTH;
|
||||||
|
}
|
||||||
|
itc_msg.addr = address;
|
||||||
|
itc_msg.flags = I2C_MSG_READ;
|
||||||
|
itc_msg.length = num_bytes;
|
||||||
|
itc_msg.data = &rx_buf[rx_buf_idx];
|
||||||
|
process();
|
||||||
|
rx_buf_len += itc_msg.xferred;
|
||||||
|
itc_msg.flags = 0;
|
||||||
|
return rx_buf_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8 WireBase::requestFrom(int address, int numBytes) {
|
||||||
|
return WireBase::requestFrom((uint8)address, numBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WireBase::write(uint8 value) {
|
||||||
|
if (tx_buf_idx == BUFFER_LENGTH) {
|
||||||
|
tx_buf_overflow = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
tx_buf[tx_buf_idx++] = value;
|
||||||
|
itc_msg.length++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WireBase::write(uint8* buf, int len) {
|
||||||
|
for (uint8 i = 0; i < len; i++) {
|
||||||
|
write(buf[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WireBase::write(int value) {
|
||||||
|
write((uint8)value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WireBase::write(int* buf, int len) {
|
||||||
|
write((uint8*)buf, (uint8)len);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WireBase::write(char* buf) {
|
||||||
|
uint8 *ptr = (uint8*)buf;
|
||||||
|
while (*ptr) {
|
||||||
|
write(*ptr);
|
||||||
|
ptr++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8 WireBase::available() {
|
||||||
|
return rx_buf_len - rx_buf_idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8 WireBase::read() {
|
||||||
|
if (rx_buf_idx == rx_buf_len) {
|
||||||
|
rx_buf_idx = 0;
|
||||||
|
rx_buf_len = 0;
|
||||||
|
return 0;
|
||||||
|
} else if (rx_buf_idx == (rx_buf_len-1)) {
|
||||||
|
uint8 temp = rx_buf[rx_buf_idx];
|
||||||
|
rx_buf_idx = 0;
|
||||||
|
rx_buf_len = 0;
|
||||||
|
return temp;
|
||||||
|
}
|
||||||
|
return rx_buf[rx_buf_idx++];
|
||||||
|
}
|
|
@ -0,0 +1,145 @@
|
||||||
|
/******************************************************************************
|
||||||
|
* The MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2010 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 WireBase.h
|
||||||
|
* @author Trystan Jones <crenn6977@gmail.com>
|
||||||
|
* @brief Wire library, following the majority of the interface from Arduino.
|
||||||
|
* Provides a 'standard' interface to I2C (two-wire) communication for
|
||||||
|
* derived classes.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Library created by crenn to allow a system which would provide users the
|
||||||
|
* 'standardised' Arduino method for interfacing with I2C devices regardless of
|
||||||
|
* whether it is I2C hardware or emulating software.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _WIREBASE_H_
|
||||||
|
#define _WIREBASE_H_
|
||||||
|
|
||||||
|
#include "wirish.h"
|
||||||
|
#include <libmaple/i2c_slave.h>
|
||||||
|
|
||||||
|
#define BUFFER_LENGTH 32
|
||||||
|
|
||||||
|
/* return codes from endTransmission() */
|
||||||
|
#define SUCCESS 0 /* transmission was successful */
|
||||||
|
#define EDATA 1 /* too much data */
|
||||||
|
#define ENACKADDR 2 /* received nack on transmit of address */
|
||||||
|
#define ENACKTRNS 3 /* received nack on transmit of data */
|
||||||
|
#define EOTHER 4 /* other error */
|
||||||
|
|
||||||
|
class WireBase { // Abstraction is awesome!
|
||||||
|
protected:
|
||||||
|
i2c_msg itc_msg;
|
||||||
|
uint8 rx_buf[BUFFER_LENGTH]; /* receive buffer */
|
||||||
|
uint8 rx_buf_idx; /* first unread idx in rx_buf */
|
||||||
|
uint8 rx_buf_len; /* number of bytes read */
|
||||||
|
|
||||||
|
uint8 tx_buf[BUFFER_LENGTH]; /* transmit buffer */
|
||||||
|
uint8 tx_buf_idx; // next idx available in tx_buf, -1 overflow
|
||||||
|
boolean tx_buf_overflow;
|
||||||
|
|
||||||
|
// Force derived classes to define process function
|
||||||
|
virtual uint8 process(uint8) = 0;
|
||||||
|
virtual uint8 process() = 0;
|
||||||
|
public:
|
||||||
|
WireBase() {}
|
||||||
|
~WireBase() {}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Initialises the class interface
|
||||||
|
*/
|
||||||
|
// Allow derived classes to overwrite begin function
|
||||||
|
virtual void begin(uint8 = 0x00);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Sets up the transmission message to be processed
|
||||||
|
*/
|
||||||
|
void beginTransmission(uint8);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Allow only 8 bit addresses to be used
|
||||||
|
*/
|
||||||
|
void beginTransmission(int);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Call the process function to process the message if the TX
|
||||||
|
* buffer has not overflowed.
|
||||||
|
*/
|
||||||
|
uint8 endTransmission(bool);
|
||||||
|
uint8 endTransmission(void);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Request bytes from a slave device and process the request,
|
||||||
|
* storing into the receiving buffer.
|
||||||
|
*/
|
||||||
|
uint8 requestFrom(uint8, int);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Allow only 8 bit addresses to be used when requesting bytes
|
||||||
|
*/
|
||||||
|
uint8 requestFrom(int, int);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Stack up bytes to be sent when transmitting
|
||||||
|
*/
|
||||||
|
void write(uint8);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Stack up bytes from the array to be sent when transmitting
|
||||||
|
*/
|
||||||
|
void write(uint8*, int);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Ensure that a sending data will only be 8-bit bytes
|
||||||
|
*/
|
||||||
|
void write(int);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Ensure that an array sending data will only be 8-bit bytes
|
||||||
|
*/
|
||||||
|
void write(int*, int);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Stack up bytes from a string to be sent when transmitting
|
||||||
|
*/
|
||||||
|
void write(char*);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return the amount of bytes that is currently in the receiving buffer
|
||||||
|
*/
|
||||||
|
uint8 available();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return the value of byte in the receiving buffer that is currently being
|
||||||
|
* pointed to
|
||||||
|
*/
|
||||||
|
uint8 read();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // _WIREBASE_H_
|
Binary file not shown.
Loading…
Reference in New Issue